最近在把一套网页操作的接口从原来Android5.0上移植到Android7.0上。

客户端连接验证的时候主页显示异常

502 Bad Gateway The CGI was not CGI/1.1 compliant

从板子的串口上看到log显示为

[02/Jan/1970:17:13:49 +0000] cgi_header: unable to find LFLF

这部分log为boa里面打印出来的,跟踪源码在cgi_header.c关键函数如下

  45 /* TODO:
46 We still need to cycle through the data before the end of the headers,
47 line-by-line, and check for any problems with the CGI
48 outputting overriding http responses, etc...
49 */
50
51 int process_cgi_header(request * req)
52 {
53 char *buf;
54 char *c;
55
56 if (req->cgi_status != CGI_DONE)
57 req->cgi_status = CGI_BUFFER;
58
59 buf = req->header_line;
60
61 c = strstr(buf, "\n\r\n");
62 if (c == NULL) {
63 c = strstr(buf, "\n\n");
64 if (c == NULL) {
65 log_error_time();
66 fputs("cgi_header: unable to find LFLF\n", stderr);
67 #ifdef FASCIST_LOGGING
68 log_error_time();
69 fprintf(stderr, "\"%s\"\n", buf);
70 #endif
71 send_r_bad_gateway(req);
72 return 0;
73 }
74 }
75 if (req->simple) {
76 if (*(c + 1) == '\r')
77 req->header_line = c + 2;
78 else
79 req->header_line = c + 1;
80 return 1;
81 }

该函数

process_cgi_header
是boa父进程fork出子进去去执行对应的cgi程序后,通过管道来获取cgi执行的输出内容进行解析,从而对客户端进行相应。
在src/pipe.c调用如下:
  27 /*
28 * Name: read_from_pipe
29 * Description: Reads data from a pipe
30 *
31 * Return values:
32 * -1: request blocked, move to blocked queue
33 * 0: EOF or error, close it down
34 * 1: successful read, recycle in ready queue
35 */
36
37 int read_from_pipe(request * req)
38 {
39 int bytes_read, bytes_to_read =
40 BUFFER_SIZE - (req->header_end - req->buffer);
41
42 if (bytes_to_read == 0) { /* buffer full */
43 if (req->cgi_status == CGI_PARSE) { /* got+parsed header */
44 req->cgi_status = CGI_BUFFER;
45 *req->header_end = '\0'; /* points to end of read data */
46 /* Could the above statement overwrite data???
47 No, because req->header_end points to where new data
48 should begin, not where old data is.
49 */
50 return process_cgi_header(req); /* cgi_status will change */
51 }
52 req->status = PIPE_WRITE;
53 return 1;
54 }
55
56 bytes_read = read(req->data_fd, req->header_end, bytes_to_read);
57 #ifdef FASCIST_LOGGING
58 if (bytes_read > 0) {
59 *(req->header_end + bytes_read) = '\0';
60 fprintf(stderr, "pipe.c - read %d bytes: \"%s\"\n",
61 bytes_read, req->header_end);
62 } else
63 fprintf(stderr, "pipe.c - read %d bytes\n", bytes_read);
64 fprintf(stderr, "status, cgi_status: %d, %d\n", req->status,
65 req->cgi_status);
66 #endif
67
68 if (bytes_read == -1) {
69 if (errno == EINTR)
70 return 1;
71 else if (errno == EWOULDBLOCK || errno == EAGAIN)
72 return -1; /* request blocked at the pipe level, but keep going */
73 else {
74 req->status = DEAD;
75 log_error_doc(req);
76 perror("pipe read");
77 return 0;
78 }
79 } else if (bytes_read == 0) { /* eof, write rest of buffer */
80 req->status = PIPE_WRITE;
81 if (req->cgi_status == CGI_PARSE) { /* hasn't processed header yet */
82 req->cgi_status = CGI_DONE;
83 *req->header_end = '\0'; /* points to end of read data */
84 return process_cgi_header(req); /* cgi_status will change */
85 }
86 req->cgi_status = CGI_DONE;
87 return 1;
88 }
89 req->header_end += bytes_read;
90 return 1;
91 }
92

这部分代码中有一个宏

FASCIST_LOGGING

加上这个宏重新编译验证可以看到更信息的log

[02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Host: 192.168.49.1"
[02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Upgrade-Insecure-Requests: 1"
[02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
[02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1"
[02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Accept-Language: zh-cn"
[02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Accept-Encoding: gzip, deflate"
[02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Connection: keep-alive"
external/boa/src/alias.c:150 - comparing "/cgi-bin/home.cgi" (request) to "/cgi-bin/" (alias): Got it!
[02/Jan/1970:17:13:49 +0000] external/boa/src/alias.c:414 - pathname in init_script_alias is: "/data/boa/www/cgi-bin/home.cgi" ("home.cgi")
external/boa/src/cgi.c - environment variable for cgi: "PATH=/bin:/usr/bin:/usr/local/bin"
external/boa/src/cgi.c - environment variable for cgi: "SERVER_SOFTWARE=Boa/0.94.13"
external/boa/src/cgi.c - environment variable for cgi: "SERVER_NAME=www.lollipop.com"
external/boa/src/cgi.c - environment variable for cgi: "GATEWAY_INTERFACE=CGI/1.1"
external/boa/src/cgi.c - environment variable for cgi: "SERVER_PORT=80"
external/boa/src/cgi.c - environment variable for cgi: "SERVER_ADMIN="
external/boa/src/cgi.c - environment variable for cgi: "HTTP_HOST=192.168.49.1"
external/boa/src/cgi.c - environment variable for cgi: "HTTP_UPGRADE_INSECURE_REQUESTS=1"
external/boa/src/cgi.c - environment variable for cgi: "HTTP_USER_AGENT=Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1"
external/boa/src/cgi.c - environment variable for cgi: "HTTP_ACCEPT_LANGUAGE=zh-cn"
external/boa/src/cgi.c - environment variable for cgi: "HTTP_ACCEPT_ENCODING=gzip, deflate"
external/boa/src/cgi.c - environment variable for cgi: "REQUEST_METHOD=GET"
external/boa/src/cgi.c - environment variable for cgi: "SERVER_ADDR=192.168.49.1"
external/boa/src/cgi.c - environment variable for cgi: "SERVER_PROTOCOL=HTTP/1.1"
external/boa/src/cgi.c - environment variable for cgi: "REQUEST_URI=/cgi-bin/home.cgi"
external/boa/src/cgi.c - environment variable for cgi: "SCRIPT_NAME=/cgi-bin/home.cgi"
external/boa/src/cgi.c - environment variable for cgi: "REMOTE_ADDR=192.168.49.77"
external/boa/src/cgi.c - environment variable for cgi: "REMOTE_PORT=54608"
pipe.c - read -1 bytes
status, cgi_status: 7, 1
pipe.c - read 0 bytes
status, cgi_status: 7, 1
[02/Jan/1970:17:13:49 +0000] cgi_header: unable to find LFLF
[02/Jan/1970:17:13:49 +0000] ""
[02/Jan/1970:17:13:49 +0000] external/boa/src/buffer.c:200 - Wrote "HTTP/1.0 502 Bad Gateway
Date: Fri, 02 Jan 1970 17:13:49 GMT
Server: Boa/0.94.13
Connection: close
Content-Type: text/html; charset=ISO-8859-1 <HTML><HEAD><TITLE>502 Bad Gateway</TITLE></HEAD>
<BODY><H1>502 Bad Gateway</H1>
The CGI was not CGI/1.1 compliant.
</BODY></HTML>
" (281 bytes)

从log上看出读取pipe的时候第一次读取失败返回来-1,第二次读取到0 说明管道里没有读取到东西,可能是cgi没有往管道里写东西。

查看boa是如何执行对应的cgi并从cgi获取输出信息的

在cgi.c里面有如下代码

 346 /*
347 * Name: init_cgi
348 *
349 * Description: Called for GET/POST requests that refer to ScriptAlias
350 * directories or application/x-httpd-cgi files. Ties stdout to socket,
351 * stdin to data if POST, and execs CGI.
352 * stderr remains tied to our log file; is this good?
353 *
354 * Returns:
355 * 0 - error or NPH, either way the socket is closed
356 * 1 - success
357 */
358
359 int init_cgi(request * req)
360 {
361 int child_pid;
362 int pipes[2];
363 int use_pipes = 0;
364
365 SQUASH_KA(req);
366
367 if (req->is_cgi) {
368 if (complete_env(req) == 0) {
369 return 0;
370 }
371 }
372 #ifdef FASCIST_LOGGING
373 {
374 int i;
375 for (i = 0; i < req->cgi_env_index; ++i)
376 fprintf(stderr, "%s - environment variable for cgi: \"%s\"\n",
377 __FILE__, req->cgi_env[i]);
378 }
379 #endif
380
381 if (req->is_cgi == CGI || 1) {
382 use_pipes = 1;
383 if (pipe(pipes) == -1) {
384 log_error_time();
385 perror("pipe");
386 return 0;
387 }
388
389 /* set the read end of the socket to non-blocking */
390 if (set_nonblock_fd(pipes[0]) == -1) {
391 log_error_time();
392 perror("cgi-fcntl");
393 close(pipes[0]);
394 close(pipes[1]);
395 return 0;
396 }
397 }
398
399 child_pid = fork();
400 switch(child_pid) {
401 case -1:
402 /* fork unsuccessful */
403 log_error_time();
404 perror("fork");
405
406 if (use_pipes) {
407 close(pipes[0]);
408 close(pipes[1]);
409 }
410 send_r_error(req);
411 /* FIXME: There is aproblem here. send_r_error would work
412 for NPH and CGI, but not for GUNZIP. Fix that. */
413 /* i'd like to send_r_error, but.... */
414 return 0;
415 break;
416 case 0:
417 /* child */
418 if (req->is_cgi == CGI || req->is_cgi == NPH) {
419 char *foo = strdup(req->pathname);
420 char *c;
421
422 if (!foo) {
423 WARN("unable to strdup pathname for req->pathname");
424 _exit(1);
425 }
426 c = strrchr(foo, '/');
427 if (c) {
428 ++c;
429 *c = '\0';
430 } else {
431 /* we have a serious problem */
432 log_error_time();
433 perror("chdir");
434 if (use_pipes)
435 close(pipes[1]);
436 _exit(1);
437 }
438 if (chdir(foo) != 0) {
439 log_error_time();
440 perror("chdir");
441 if (use_pipes)
442 close(pipes[1]);
443 _exit(1);
444 }
445 }
446 if (use_pipes) {
447 close(pipes[0]);
448 /* tie cgi's STDOUT to it's write end of pipe */
449 if (dup2(pipes[1], STDOUT_FILENO) == -1) {
450 log_error_time();
451 perror("dup2 - pipes");
452 close(pipes[1]);
453 _exit(1);
454 }
455 close(pipes[1]);
456 if (set_block_fd(STDOUT_FILENO) == -1) {
457 log_error_time();
458 perror("cgi-fcntl");
459 _exit(1);
460 }
461 } else {
462 /* tie stdout to socket */
463 if (dup2(req->fd, STDOUT_FILENO) == -1) {
464 log_error_time();
465 perror("dup2 - fd");
466 _exit(1);
467 }
468 /* Switch socket flags back to blocking */
469 if (set_block_fd(req->fd) == -1) {
470 log_error_time();
471 perror("cgi-fcntl");
472 _exit(1);
473 }
474 }
475 /* tie post_data_fd to POST stdin */
476 if (req->method == M_POST) { /* tie stdin to file */
477 lseek(req->post_data_fd, SEEK_SET, 0);
478 dup2(req->post_data_fd, STDIN_FILENO);
479 close(req->post_data_fd);
480 }
481 /* Close access log, so CGI program can't scribble
482 * where it shouldn't
483 */
484 close_access_log();
485
486 /*
487 * tie STDERR to cgi_log_fd
488 * cgi_log_fd will automatically close, close-on-exec rocks!
489 * if we don't tied STDERR (current log_error) to cgi_log_fd,
490 * then we ought to close it.
491 */
492 if (!cgi_log_fd)
493 dup2(devnullfd, STDERR_FILENO);
494 else
495 dup2(cgi_log_fd, STDERR_FILENO);
496
497 if (req->is_cgi) {
498 char *aargv[CGI_ARGC_MAX + 1];
499 create_argv(req, aargv);
500 execve(req->pathname, aargv, req->cgi_env);
501 } else {
502 if (req->pathname[strlen(req->pathname) - 1] == '/')
503 execl(dirmaker, dirmaker, req->pathname, req->request_uri,
504 NULL);
505 #ifdef GUNZIP
506 else
507 execl(GUNZIP, GUNZIP, "--stdout", "--decompress",
508 req->pathname, NULL);
509 #endif
510 }
511 /* execve failed */
512 WARN(req->pathname);
513 _exit(1);
514 break;
515
516 default:
517 /* parent */
518 /* if here, fork was successful */
519 if (verbose_cgi_logs) {
520 log_error_time();
521 fprintf(stderr, "Forked child \"%s\" pid %d\n",
522 req->pathname, child_pid);
523 }
524
525 if (req->method == M_POST) {
526 close(req->post_data_fd); /* child closed it too */
527 req->post_data_fd = 0;
528 }
529
530 /* NPH, GUNZIP, etc... all go straight to the fd */
531 if (!use_pipes)
532 return 0;
533
534 close(pipes[1]);
535 req->data_fd = pipes[0];
536
537 req->status = PIPE_READ;
538 if (req->is_cgi == CGI) {
539 req->cgi_status = CGI_PARSE; /* got to parse cgi header */
540 /* for cgi_header... I get half the buffer! */
541 req->header_line = req->header_end =
542 (req->buffer + BUFFER_SIZE / 2);
543 } else {
544 req->cgi_status = CGI_BUFFER;
545 /* I get all the buffer! */
546 req->header_line = req->header_end = req->buffer;
547 }
548
549 /* reset req->filepos for logging (it's used in pipe.c) */
550 /* still don't know why req->filesize might be reset though */
551 req->filepos = 0;
552 break;
553 }
554
555 return 1;
556 }

从这部分代码可以知道boa在fork出子进程去执行对应的cgi的时候将子进程的标准输出dup到了提前创建好的pipe,父进程则从pipe的另一端读取cgi的输出内容(既cgi程序printf输出的内容)。

现在需要去看下对应的cgi是否有内容输出来

home.c编译成home.cgi

main函数如下

 116 int main(void)
117 {
118 char mode[32] = {0};
119
120 memset(mode, 0, sizeof(mode));
121 lollipopConfInit();
122 getLollipopConf("function_mode", mode);
123 lollipopConfDeinit();
124
125 126 if (!strcmp(mode, "WFD")) {
127 return p2p_state();
128 } else if (!strcmp(mode, "DLNA") || !strcmp(mode, "MIX")) {
129 return ap_state();
130 } else {
131 return -1;
132 }
133 }
  24 int p2p_state(void)
25 {
26 struct list_head *pList;
27 struct list_head groupList;
28
29 printf("Content-type: text/html\n\n");
30 printf("<HTML><BODY>\n");
31 printf("<HEAD>\n");
32 printf("<TITLE>Lollipop Home</TITLE>\n");
33 printf("<link rel='stylesheet' type='text/css' href='css/main.css' />");
34 printf("</HEAD>\n");
35
36 printf("<p>");
37 printf("<a href=\"wifi.cgi\"><img border=\"0\" src=\"/res/icon_wifi_01.png\" /></a>\n");
38 printf("touch icon to show wifi direct group list");
39 printf("</p>");
40 printf("<br />\n");
41 printf("<br />\n");
........... 73 int ap_state(void)
74 {
75 struct list_head apList;
76 struct list_head *pList;
77
78 printf("Content-type:text/html;charset=utf-8\n\n"); //response header
79 printf("<HTML><BODY>\n");
80 printf("<HEAD>\n");
81 printf("<TITLE>Lollipop Home</TITLE>\n");
82 printf("<link rel='stylesheet' type='text/css' href='/css/main.css' />");
83 printf("</HEAD>\n");
84
85 printf("<div class='content_icon'>\n");
86 printf("<p><a href='wifi.cgi' class='icon_wifi'>WiFi AP</a></p>\n");
87 printf("</div>\n\n");
88
89
.....

在这里需要获取mode类型,然后输出不同的内容

检查过p2p_state()及ap_state()函数都是由正常的格式内容打印出来,而且是在原来的平台验证过的.

但是main里面还有一个else的分支简单粗暴的返回了一个 -1.

添加一个log信息到else信息里重新验证了一次,果然main函数跑到了else分支,也就是直接返回-1而没有printf任何内容,这样boa进程则无法从home.cgi读取到任何输出内容。

作为一个cgi程序响应客户端的GET请求,如果不是正确的请求个人认为至少应该回复一组正确的消息,只是在内容上可以显示一些提示信息,而不能像普通的程序一样直接返回一个-1.

这会导致客户端看到的错误信息与你想表达的意思不一致。

所以在这只需要添加一个notFound的返回消息

 141 void not_found(void)
142 {
143 printf("Content-type:text/html;charset=utf-8\n\n"); //response header
144 printf("<HTML><BODY>\n");
145 printf("<HEAD>\n");
146 printf("<TITLE>Lollipop Home</TITLE>\n");
147 printf("<link rel='stylesheet' type='text/css' href='/css/main.css' />");
148 printf("</HEAD>\n");
149
150 printf("<p>Not Support Mode</a></p>\n");
151
152 printf("</BODY>\n");
153 printf("</HTML>\n");
154
155 }
156
157 int main(void)
158 {
159 char mode[32] = {0};
160
161 memset(mode, 0, sizeof(mode));
162 lollipopConfInit();
163 getLollipopConf("function_mode", mode);
164 lollipopConfDeinit();
165 if (!strcmp(mode, "WFD")) {
166 return p2p_state();
167 } else if (!strcmp(mode, "DLNA") || !strcmp(mode, "MIX") || !strcmp(mode, "AIRPLAY")) {
168 return ap_state();
169 } else {
170 not_found();
171 }
172 return 0;
173 }



解决boa网页操作出现502 Bad Gateway The CGI was not CGI/1.1 compliant的一种可能的更多相关文章

  1. ngnix 502 bad gateway 的解决办法之空间满了

    网站一直运行都很正常,但某天登录后台却出现502 bad gateway,上网搜索都是一大堆解决办法,没可操作性.网站难道出现安全问题?这个应该很少概率.最后排查发现空间满了. 使用命令:df -hl ...

  2. 解决 504 Gateway Time-out和502 Bad Gateway(nginx)

    504 Gateway Time-out 问题所在: 所请求的网关没有请求到,简单来说就是没有请求到可以执行的PHP-CGI. 一般看来, 这种情况可能是由于nginx默认的fastcgi进程响应的缓 ...

  3. Nginx 502 Bad Gateway 错误的原因及解决方法

    http://my.oschina.net/zhouyuan/blog/118708 刚才在调试程序的时候,居然服务器502错误,昨天晚上也发生了,好像我没有做非常规的操作. 然后网上寻找了下答案, ...

  4. 【亲测】502 Bad Gateway 怎么解决?

    502 Bad Gateway 怎么解决? 1.什么是502 badgateway 报错 简单来说 502 是报错类型代码,bad gateway 错误的网关. 2.产生错误的原因 连接超时 具体原因 ...

  5. (总结)Nginx 502 Bad Gateway错误触发条件与解决方法

    一些运行在Nginx上的网站有时候会出现“502 Bad Gateway”错误,有些时候甚至频繁的出现.以下是从Google搜集整理的一些Nginx 502错误的排查方法,供参考: Nginx 502 ...

  6. 502 Bad Gateway 怎么解决?

    出现502的原因是:对用户访问请求的响应超时造成的 服务端解决办法: 1.提高 Web 服务器的响应速度,也即减少内部的调用关系,可以把需要的页面.素材或数据,缓存在内存中,可以是专门的缓存服务器 , ...

  7. phpStorm怎么解决502 bad gateway(总结整理)

    phpStorm怎么解决502 bad gateway(总结整理) 一.总结 1.配置 php解释器. 二.phpStorm解释器与服务器配置(解决502 bad gateway与404 not fo ...

  8. Nginx 502 Bad Gateway 错误的解决方法

    502 bad gateway 的解决方法 通用配置 proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小 proxy_buffers 4 32k; # ...

  9. Nginx + php-fpm 执行 PHP 脚本超时 报错 502 Bad Gateway + 504 Gateway Time-out 的解决办法

    上周写好的发送邮件的计划任务只发送了一部分,检查计划任务日志,发现 502 Bad Gateway 的错误(已经在脚本中设置了 set_time_limit(0)). 后来在网上查找资料,可以通过以下 ...

随机推荐

  1. Docker 网络类型

    Docker 网络类型 前言 a. 本文主要为 Docker的视频教程 笔记. b. 环境为 CentOS 7.0 云服务器 c. 上一篇:docker-compose 的使用和负载均衡的初探 1. ...

  2. php时间区间,优化显示

    <?php /** * 类似微信的时间显示 * 规则是:今天的,显示几秒前,几分钟前,几小时前,昨天的显示昨天 上午 XX:XX * 再往前,本周的,显示周几+时间,再往前,本年的,显示月日+时 ...

  3. win7任务计划提示”该任务映像已损坏或已篡改“

    打开任务计划,弹出了下面的对话框[该任务映像已损坏或已篡改.(异常来自HRESULT:0x80041321)] 首先你以管理员的身份运行cmd命令,打开运行窗口 输入:chcp 437,并回车,回车后 ...

  4. Dapr实战(二) 服务调用

    服务调用是什么 在分布式应用程序中的服务之间进行调用会涉及到许多挑战. 例如: 维护其他服务的地址. 如何安全地调用服务. 在发生短暂的 暂时性错误 时如何处理重试. 分布式应用程序调用链路追踪. 服 ...

  5. python的列表和java的数组有何异同

    今天面试被问到,自己学习一下. python的列表是可变长的,定义时不需要指定长度:pyhton是弱对象类型,python的列表存储的数据类型可以不相同:python的列表更加灵活,如可以通过''命令 ...

  6. 『Python』matplotlib坐标轴应用

    1. 设置坐标轴的位置和展示形式 import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl mpl.use ...

  7. sunny 内网穿透使用。

    启动方法:

  8. Python实现一个简单三层神经网络的搭建并测试

    python实现一个简单三层神经网络的搭建(有代码) 废话不多说了,直接步入正题,一个完整的神经网络一般由三层构成:输入层,隐藏层(可以有多层)和输出层.本文所构建的神经网络隐藏层只有一层.一个神经网 ...

  9. 记一次centos挂载ceph存储的坑

    起因 生产有两台服务器,准备用来跑工作流,执行的资源的是放在ceph存储集群中,第一步挂载ceph 执行命令:mount -t ceph xxx:xxx -o name=admin,secret=AQ ...

  10. 升级sudo版本

    1.查看sudo版本 sudo -V 2.下载sudo最新安装文件 sudo官方地址: https://www.sudo.ws/ 下载地址:https://www.sudo.ws/dist/ 3.解压 ...