解决boa网页操作出现502 Bad Gateway The CGI was not CGI/1.1 compliant的一种可能
最近在把一套网页操作的接口从原来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的一种可能的更多相关文章
- ngnix 502 bad gateway 的解决办法之空间满了
网站一直运行都很正常,但某天登录后台却出现502 bad gateway,上网搜索都是一大堆解决办法,没可操作性.网站难道出现安全问题?这个应该很少概率.最后排查发现空间满了. 使用命令:df -hl ...
- 解决 504 Gateway Time-out和502 Bad Gateway(nginx)
504 Gateway Time-out 问题所在: 所请求的网关没有请求到,简单来说就是没有请求到可以执行的PHP-CGI. 一般看来, 这种情况可能是由于nginx默认的fastcgi进程响应的缓 ...
- Nginx 502 Bad Gateway 错误的原因及解决方法
http://my.oschina.net/zhouyuan/blog/118708 刚才在调试程序的时候,居然服务器502错误,昨天晚上也发生了,好像我没有做非常规的操作. 然后网上寻找了下答案, ...
- 【亲测】502 Bad Gateway 怎么解决?
502 Bad Gateway 怎么解决? 1.什么是502 badgateway 报错 简单来说 502 是报错类型代码,bad gateway 错误的网关. 2.产生错误的原因 连接超时 具体原因 ...
- (总结)Nginx 502 Bad Gateway错误触发条件与解决方法
一些运行在Nginx上的网站有时候会出现“502 Bad Gateway”错误,有些时候甚至频繁的出现.以下是从Google搜集整理的一些Nginx 502错误的排查方法,供参考: Nginx 502 ...
- 502 Bad Gateway 怎么解决?
出现502的原因是:对用户访问请求的响应超时造成的 服务端解决办法: 1.提高 Web 服务器的响应速度,也即减少内部的调用关系,可以把需要的页面.素材或数据,缓存在内存中,可以是专门的缓存服务器 , ...
- phpStorm怎么解决502 bad gateway(总结整理)
phpStorm怎么解决502 bad gateway(总结整理) 一.总结 1.配置 php解释器. 二.phpStorm解释器与服务器配置(解决502 bad gateway与404 not fo ...
- Nginx 502 Bad Gateway 错误的解决方法
502 bad gateway 的解决方法 通用配置 proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小 proxy_buffers 4 32k; # ...
- Nginx + php-fpm 执行 PHP 脚本超时 报错 502 Bad Gateway + 504 Gateway Time-out 的解决办法
上周写好的发送邮件的计划任务只发送了一部分,检查计划任务日志,发现 502 Bad Gateway 的错误(已经在脚本中设置了 set_time_limit(0)). 后来在网上查找资料,可以通过以下 ...
随机推荐
- CodeForce-801C Voltage Keepsake(二分)
题目大意:有n个装备,每个设备耗能为每单位时间耗能ai,初始能量为bi;你有一个充电宝,每单位时间可以冲p能量,你可以在任意时间任意拔冲. 如果可以所有设备都可以一直工作下去,输出-1:否则,输出所有 ...
- java线程day-01
综述:下面写的是我学习java线程时做的一些笔记和查阅的一些资料总结而成.大多以问答的形式出现. 一.什么是线程? 答:线程是一个轻量级的进程,现在操作系统中一个基本的调度单位,而且线程是彼此独立执行 ...
- TP5框架下实现数据库的备份功能-tp5er/tp5-databackup
1.安装扩展 方法一: composer require tp5er/tp5-databackup dev-master 方法二 composer.json: "require": ...
- Jmeter扩展组件开发(10) - 自定义扩展函数助手的开发
CODE package com.functions;import org.apache.jmeter.engine.util.CompoundVariable;import org.apache.j ...
- 安装redis3.0.5
首先在官网下载redis-3.0.5.tar.gz 在某一个要安装redis的目录下输入命令 tar xzf redis-3.0.5.tar.gz 实现解压缩 进入解压缩后的redis目录 输入mak ...
- netty系列之:使用netty搭建websocket服务器
目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketFra ...
- Redis分布式锁,看完不懂你打我
简易的redis分布式锁 加锁: set key my_random_value NX PX 30000 这个命令比setnx好,因为可以同时设置过期时间.不设置过期时间,应用挂了,解不了锁,就一直锁 ...
- C#开发BIMFACE系列41 服务端API之模型对比
BIMFACE二次开发系列目录 [已更新最新开发文章,点击查看详细] 在建筑施工图审查系统中,设计单位提交设计完成的模型/图纸,审查专家审查模型/图纸.审查过程中如果发现不符合规范的地方,则流 ...
- 分布式锁Redission
Redisson 作为分布式锁 官方文档:https://github.com/redisson/redisson/wiki 引入依赖 <dependency> <groupId&g ...
- js Promise用法
Promise 英文意思是 承诺的意思,是对将来的事情做了承诺, Promise 有三种状态, Pending 进行中或者等待中 Fulfilled 已成功 Rejected 已失败 Promise ...