从stdin / stdout读/写时,CGI程序超时

时间:2018-08-05 23:32:11

标签: c cgi

我已经从外壳测试了该程序,并且从文件中还原标准输入时它可以正常工作。但是,当作为CGI程序运行时,它会超时(TimeForCGI hiawatha Web服务器设置设置为30秒)。该程序仅包含在一个文件中。应该注意的是,编写该程序仅是为了物理验证我在阅读的有关CGI的内容,并且我选择了C(或其他会生成二进制可执行文件的东西),因此我可以确保所有解释器都不会触及这些东西,可以简化它们的抽象。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <error.h>

int main (void);

int
main
(void)
{
  static char buf[BUFSIZ];
  size_t size;

  if (setvbuf (stdout, NULL, _IOFBF, BUFSIZ) != 0)
    error (EXIT_FAILURE, errno, "setvbuf(), stdout");
  if (setvbuf (stdin, NULL, _IOFBF, BUFSIZ) != 0)
    error (EXIT_FAILURE, errno, "setvbuf(), stdin");
  if (setvbuf (stderr, NULL, _IOLBF, BUFSIZ) != 0)
    error (EXIT_FAILURE, errno, "setvbuf(), stderr");
  printf ("Content-Type: text/plain\n\n");
  if (fflush (stdout) == EOF)
    error (EXIT_FAILURE, errno, "fflush()");
  for (;;)
    {
      size = fread (buf,1, BUFSIZ, stdin);
      if (size == 0)
        {
          if (feof (stdin) != 0)
            goto quit;
          else
            error (EXIT_FAILURE, errno, "fread(), stdin");
        }
      size = fwrite (buf, 1, size, stdout);
      if (size == 0)
        error (EXIT_FAILURE, errno, "write(), stdout");
    }
 quit:
  fflush (stdout);
  return EXIT_SUCCESS;
}

这是相应的html表单;

<html>
  <head>
    <title>Form</title>
  </head>
  <body>
    <form action="form-process.cgi" method="post">
      input_a: <input name="input_a" type="text"><br>
      input_b: <input name="input_b" type="text"><br>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

3 个答案:

答案 0 :(得分:0)

您的程序尝试从其标准输入中读取数据,直到到达结束为止。当您从文件重定向输入时很好,但是对于CGI程序是不合适的。当到达请求主体的末尾时,运行CGI的Web服务器没有义务在输入中发出文件结束信号。如果没有,则您的程序将在fread()中无限期地阻塞。

有几个原因可能导致在请求正文末尾未发出EOF信号。 RFC明确地假定存在扩展数据,但是服务器将CGI的标准输入直接连接到将要发出请求的网络套接字也是合理的。除非并且除非客户端关闭了EOF,否则通常不会在其中检测到EOF。连接结束时,许多客户端在请求之间不执行 操作,其余客户端在收到响应后才执行其他操作。

因此,RFC 3875中的CGI规范说“即使有更多数据可用,脚本也绝不能尝试读取超过CONTENT_LENGTH个字节”(第4.2节)。如果请求指定一个,则通过该名称的环境变量将CONTENT_LENGTH传送到脚本。您的CGI读取的字节数不得超过变量指定的字节数,并且如果根本未指定内容长度,则其不得读取 any 字节。另一方面,CGI不需要读取整个请求正文,也可以根本不读取任何请求正文。

答案 1 :(得分:0)

与此同时,我已经做到了;

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <error.h>
#include <dstralg.h>

int main (void);

int
main
(void)
{
  int l;
  int i;

  if (setvbuf (stdin, NULL, _IOFBF, BUFSIZ)!= 0)
    error (EXIT_FAILURE, errno, "sevbuf(), stdin");
  if (setvbuf (stdout, NULL, _IOFBF, BUFSIZ)!= 0)
    error (EXIT_FAILURE, errno, "sevbuf(), stdout");
  printf ("Content-Type: text/plain\n\n");
  l = atoi (getenv ("CONTENT_LENGTH"));
  for (i = 0; i < l; ++i)
    putchar (getchar ());
  return EXIT_SUCCESS;
}

显示所需的行为。完全缓冲极大地减少了一次处理一个字符的开销,并且只有在取消释放getchar和putchar(假设libc已动态链接)后才是函数调用。因为这只是使用来自Hiawatha的数据(我相信)的实验代码,所以我不必费心检查getchar和putchar的返回值是否为错误条件。我也不必检查CONTENT_LENGTH是NULL还是“”。实际上,对于流量较小的小型项目,我会使用特定于域的解释语言,例如PHP。我可能会使用C / C ++来处理苛刻的工作负载,尽管FastCGI可以通过更轻松的打开和关闭与Unix域套接字的连接的操作来提高性能,而不是通过创建页面表和所有其他操作来进行分叉子进程的繁重操作。其他过程管理簿记。

答案 2 :(得分:0)

您上一篇文章中的以下代码也应这样做:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <error.h>
#include <dstralg.h>

int main (void);

int
main
(void)
{
    int l;
    int i;

    printf ("Content-Type: text/plain\n\n");
    l = atoi (getenv ("CONTENT_LENGTH"));
    for (i = 0; i < l; ++i)
        putchar (getchar ());
    fflush(stdout);
    return EXIT_SUCCESS;
}

,并且也没有最后的fflush(stdout);,因为您要在最后一个exit(2)之后立即进入putchar(3);,这将使 stdio 刷新所有缓冲区剩下的。