最近在把一套网页操作的接口从原来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
从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("
"); 37 printf("\n"); 38 printf("touch icon to show wifi direct group list"); 39 printf("
");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("
在这里需要获取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("
Not Support Mode
\n"); 151 152 printf("\n"); 153 printf("\n");手机扫一扫
移动阅读更方便
你可能感兴趣的文章