最近在把一套网页操作的接口从原来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. CodeForce-801C Voltage Keepsake(二分)

    题目大意:有n个装备,每个设备耗能为每单位时间耗能ai,初始能量为bi;你有一个充电宝,每单位时间可以冲p能量,你可以在任意时间任意拔冲. 如果可以所有设备都可以一直工作下去,输出-1:否则,输出所有 ...

  2. java线程day-01

    综述:下面写的是我学习java线程时做的一些笔记和查阅的一些资料总结而成.大多以问答的形式出现. 一.什么是线程? 答:线程是一个轻量级的进程,现在操作系统中一个基本的调度单位,而且线程是彼此独立执行 ...

  3. TP5框架下实现数据库的备份功能-tp5er/tp5-databackup

    1.安装扩展 方法一: composer require tp5er/tp5-databackup dev-master 方法二 composer.json: "require": ...

  4. Jmeter扩展组件开发(10) - 自定义扩展函数助手的开发

    CODE package com.functions;import org.apache.jmeter.engine.util.CompoundVariable;import org.apache.j ...

  5. 安装redis3.0.5

    首先在官网下载redis-3.0.5.tar.gz 在某一个要安装redis的目录下输入命令 tar xzf redis-3.0.5.tar.gz 实现解压缩 进入解压缩后的redis目录 输入mak ...

  6. netty系列之:使用netty搭建websocket服务器

    目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketFra ...

  7. Redis分布式锁,看完不懂你打我

    简易的redis分布式锁 加锁: set key my_random_value NX PX 30000 这个命令比setnx好,因为可以同时设置过期时间.不设置过期时间,应用挂了,解不了锁,就一直锁 ...

  8. C#开发BIMFACE系列41 服务端API之模型对比

    BIMFACE二次开发系列目录     [已更新最新开发文章,点击查看详细] 在建筑施工图审查系统中,设计单位提交设计完成的模型/图纸,审查专家审查模型/图纸.审查过程中如果发现不符合规范的地方,则流 ...

  9. 分布式锁Redission

    Redisson 作为分布式锁 官方文档:https://github.com/redisson/redisson/wiki 引入依赖 <dependency> <groupId&g ...

  10. js Promise用法

    Promise 英文意思是 承诺的意思,是对将来的事情做了承诺, Promise 有三种状态, Pending 进行中或者等待中 Fulfilled 已成功 Rejected 已失败 Promise ...