解决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, "

");                                                
  62     if (c == NULL) {                                                          
  63         c = strstr(buf, "

");                                              
  64         if (c == NULL) {                                                      
  65             log_error_time();                                                 
  66             fputs("cgi_header: unable to find LFLF
", stderr);               
  67 #ifdef FASCIST_LOGGING                                                        
  68             log_error_time();                                                 
  69             fprintf(stderr, ""%s"
", buf);                                 
  70 #endif                                                                        
  71             send_r_bad_gateway(req);                                          
  72             return 0;                                                         
  73         }                                                                     
  74     }                                                                         
  75     if (req->simple) {                                                        
  76         if (*(c + 1) == '
')                                                 
  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 = ''; /* 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) = '';                               
  60         fprintf(stderr, "pipe.c - read %d bytes: "%s"
",                   
  61                 bytes_read, req->header_end);                                 
  62     } else                                                                    
  63         fprintf(stderr, "pipe.c - read %d bytes
", bytes_read);              
  64     fprintf(stderr, "status, cgi_status: %d, %d
", 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 = ''; /* 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"
",    
 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 = '';                                                    
 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
",                   
 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

");                                    
  30     printf("<HTML><BODY>
");                                                 
  31     printf("<HEAD>
");                                                       
  32     printf("<TITLE>Lollipop Home</TITLE>
");                                 
  33     printf("<link rel='stylesheet' type='text/css' href='css/main.css' />");  
  34     printf("</HEAD>
");                                                      
  35                                                                               
  36     printf("<p>");                                                            
  37     printf("<a href="wifi.cgi"><img border="0" src="/res/icon_wifi_01.png" /></a>
");
  38     printf("touch icon to show wifi direct group list");                      
  39     printf("</p>");                                                           
  40     printf("<br />
");                                                       
  41     printf("<br />
");             
...........
                                                                              
  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

"); //response header     
  79     printf("<HTML><BODY>
");                                                 
  80     printf("<HEAD>
");                                                       
  81     printf("<TITLE>Lollipop Home</TITLE>
");                                 
  82     printf("<link rel='stylesheet' type='text/css' href='/css/main.css' />"); 
  83     printf("</HEAD>
");                                                      
  84                                                                               
  85     printf("<div class='content_icon'>
");                                   
  86     printf("<p><a href='wifi.cgi' class='icon_wifi'>WiFi AP</a></p>
");      
  87     printf("</div>

");                                                     
  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

"); //response header     
 144     printf("<HTML><BODY>
");                                                 
 145     printf("<HEAD>
");                                                       
 146     printf("<TITLE>Lollipop Home</TITLE>
");                                 
 147     printf("<link rel='stylesheet' type='text/css' href='/css/main.css' />"); 
 148     printf("</HEAD>
");                                                      
 149                                                                               
 150     printf("<p>Not Support Mode</a></p>
");                                         
 151                                                                               
 152     printf("</BODY>
");                                                      
 153     printf("</HTML>
");                                                      
 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 }                    






原文地址:https://www.cnblogs.com/tid-think/p/11652472.html