tinyhttp源码剖析(三) :accept_request() 和 getline()

本文深入剖析tinyhttpd服务器的`accept_request()`函数,详细解释了HTTP请求报文的结构,包括请求行、消息报头和请求正文。文章还介绍了`accept_request()`的具体流程,如处理GET和POST请求的方法,以及如何根据请求方法执行相应操作,如执行CGI或提供文件服务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

继startup之后,主函数进入无限循环,首先调用accept函数。

accept原型:
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
返回:成功返回非负描述符,出错返回-1

如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户端的TCP连接。
之后创建一个新线程通过accept_request处理请求。
accept_request()如下:

void *accept_request(void *client1)
{
 int client = *(int *)client1;
 char buf[1024]; //缓冲区
 int numchars;  //读取字符计数
 char method[255];  //http请求method
 char url[255];
 char path[512];
 size_t i, j;
 struct stat st;
 int cgi = 0;      /* becomes true if server decides this is a CGI
                    * program */
 char *query_string = NULL;

 numchars = get_line(client, buf, sizeof(buf));
 i = 0; j = 0;
 while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
 {
  method[i] = buf[j];
  i++; j++;
 }
 method[i] = '\0';

 if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
 {
  unimplemented(client);
  return NULL;
 }

 if (strcasecmp(method, "POST") == 0)
  cgi = 1;

 i = 0;
 while (ISspace(buf[j]) && (j < sizeof(buf)))
  j++;
 while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
 {
  url[i] = buf[j];
  i++; j++;
 }
 url[i] = '\0';

 if (strcasecmp(method, "GET") == 0)
 {
  query_string = url;
  while ((*query_string != '?') && (*query_string != '\0'))
   query_string++;
  if (*query_string == '?')
  {
   cgi = 1;
   *query_string = '\0';
   query_string++;
  }
 }

 sprintf(path, "htdocs%s", url);
 if (path[strlen(path) - 1] == '/')
  strcat(path, "index.html");
 if (stat(path, &st) == -1) {
  while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */
   numchars = get_line(client, buf, sizeof(buf));
  not_found(client);
 }
 else
 {
  if ((st.st_mode & S_IFMT) == S_IFDIR)
   strcat(path, "/index.html");
  if ((st.st_mode & S_IXUSR) ||
      (st.st_mode & S_IXGRP) ||
      (st.st_mode & S_IXOTH)    )
   cgi = 1;
  if (!cgi)
   serve_file(client, path);
  else
   execute_cgi(client, path, method, query_string);
 }

 close(client);
 return NULL;
}

http请求报文:

http请求报文由请求行、消息报头和请求正文组成。


Request Line<CRLF> //请求行
Header-Name: header-value<CRLF>  //消息报头,一个或者多个
Header-Name: header-value<CRLF>
...
<CRLF> //空行
body//请求正文
  1. 请求行:
    Method Request_URI HTTP-Version

    以方法符为开头,空格分开,后面跟着请求URI和协议版本。其中请求方法有GET、POST、HEAD、PUT、DELETE、TRACE、CONNECT和OPTIONS等。
    GET:请求获取Request-URI所标识资源

    GET /form.html HTTP/1.1

    POST: 请求服务器接受附在后面的数据。

    POST /reg.jsp HTTP/
  2. 消息报头:
    由关键字/值对组成,每行一对
    User-Agent : 产生请求的浏览器类型
    Accept : 客户端希望接受的数据类型,比如 Accept:text/xml(application/json)表示希望接受到的是xml(json)类型
    Content-Type:发送端发送的实体数据的数据类型。
    比如,Content-Type:text/html(application/json)表示发送的是html类型。
    Host : 请求的主机名,允许多个域名同处一个IP地址,即虚拟主机

accept_request() 具体流程

  1. 使用geiline读取http头第一行并将method存入method数组。
int get_line(int sock, char *buf, int size)
{
 int i = 0;
 char c = '\0';
 int n;

 while ((i < size - 1) && (c != '\n'))
 {
  n = recv(sock, &c, 1, 0);
  /* DEBUG printf("%02X\n", c); */
  if (n > 0)
  {
   if (c == '\r')
   {
    n = recv(sock, &c, 1, MSG_PEEK);
    /* DEBUG printf("%02X\n", c); */
    if ((n > 0) && (c == '\n'))
     recv(sock, &c, 1, 0);
    else
     c = '\n';
   }
   buf[i] = c;
   i++;
  }
  else
   c = '\n';
 }
 buf[i] = '\0';
 
 return(i);
}
  1. 判断请求方法是否为GET或者POST,如果都不是调用unimplemented告知不支持该请求方法;
  2. 如果请求为PSOT,则cgi置1;
  3. 跳过空格,提取URL链接。
  4. 如果是GET请求,在GET请求中,请求参数和对应的值附加在URL后面,利用一个问号(“?”)代表URL的结尾与请求参数的开始,传递参数长度受限制。如index.jsp?10023,其中10023就是要传递 的参数。
  5. 如果找到了该请求,开启cgi,并保存URL,之后将其与请求地址的主页索引合并,形成path。
  6. stat是获取文件信息函数,失败返回-1,成功返回0。如果不存在,处理并丢弃剩下的请求头并返回错误信息。
  7. 如果文件存在但却是个目录,则继续拼接路径,默认访问这个目录下的index.html。
  8. 如果有执行权限,cgi置1,调用execut_cgi,否则调用serve_file。
    10.断开连接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值