分享

BOA代码笔记 6

 lchjczw 2012-12-26

process_requests()

昨天分析到了switch部分。

process_requests()还剩下的,也就是处理请求的switch部分和处理retval的swtich部分。先看下剩下这部分的源码:

  1.    switch (current->status) {  
  2.     case READ_HEADER:  
  3.     case ONE_CR:  
  4.     case ONE_LF:  
  5.     case TWO_CR:  
  6.         retval = read_header(current);  
  7.         break;  
  8.     case BODY_READ:  
  9.         retval = read_body(current);  
  10.         break;  
  11.     case BODY_WRITE:  
  12.         retval = write_body(current);  
  13.         break;  
  14.     case WRITE:  
  15.         retval = process_get(current);  
  16.         break;  
  17.     case PIPE_READ:  
  18.         retval = read_from_pipe(current);  
  19.         break;  
  20.     case PIPE_WRITE:  
  21.         retval = write_from_pipe(current);  
  22.         break;  
  23.     case DONE:  
  24.         /* a non-status that will terminate the request */  
  25.         retval = req_flush(current);  
  26.         /* 
  27.          * retval can be -2=error, -1=blocked, or bytes left 
  28.          */  
  29.         if (retval == -2) { /* error */  
  30.             current->status = DEAD;  
  31.             retval = 0;  
  32.         } else if (retval > 0) {  
  33.             retval = 1;  
  34.         }  
  35.         break;  
  36.     case DEAD:  
  37.         retval = 0;  
  38.         current->buffer_end = 0;  
  39.         SQUASH_KA(current);  
  40.         break;  
  41.     default:  
  42.         retval = 0;  
  43.         fprintf(stderr, "Unknown status (%d), "  
  44.                 "closing!\n", current->status);  
  45.         current->status = DEAD;  
  46.         break;  
  47.     }  
  48.   
  49. }  
  50.   
  51. if (sigterm_flag)  
  52.     SQUASH_KA(current);  
  53.   
  54. /* we put this here instead of after the switch so that 
  55.  * if we are on the last request, and get_request is successful, 
  56.  * current->next is valid! 
  57.  */  
  58. if (pending_requests)  
  59.     get_request(server_s);  
  60.   
  61. switch (retval) {  
  62. case -1:               /* request blocked */  
  63.     trailer = current;  
  64.     current = current->next;  
  65.     block_request(trailer);  
  66.     break;  
  67. case 0:                /* request complete */  
  68.     current->time_last = current_time;  
  69.     trailer = current;  
  70.     current = current->next;  
  71.     free_request(&request_ready, trailer);  
  72.     break;  
  73. case 1:                /* more to do */  
  74.     current->time_last = current_time;  
  75.     current = current->next;  
  76.     break;  
  77. default:  
  78.     log_error_time();  
  79.     fprintf(stderr, "Unknown retval in process.c - "  
  80.             "Status: %d, retval: %d\n", current->status, retval);  
  81.     current = current->next;  
  82.     break;  
  83. }  
如果状态是DONE的话,那么调用req_flush()函数清空需要向客户端发送的数据,req_flush函数上次已经分析。

如果状态是DEAD的话,那么将buffer清空,然后保活计时器清0,retval置0表示不再处理。

其他状态调用相应的处理函数。 之后再看。


之后检查一下是否收到了SIGTERM;如果有pending_requests做相应处理。

然后就到了对返回值的处理:

如果retval为-1,表示需要block,那么调用block_request(trailer);。

如果retval为0 ,表示请求不需要处理了,调用free_request(&request_ready, trailer);关闭连接,释放request结构。

如果retval为1 ,表示不需要block,但需要继续处理,那么只是简单的更新一下活跃时间time_last。



block_request()和free_requests()

block_request(trailer);先将任务从ready队列中取出,然后插入block队列。然后根据status设置相应的block_write_fdset或block_read_fdset。

free_requests的代码比较长,总体上看,功能是释放掉request占用的内存,关闭socket。大体流程是:

让request出队;

需要的话,做个记录;

如果有共享的mmap映射内存,那么引用计数-1,到0munmap掉;如果没有共享的mmap映射,检查是否有私有的mmap内存,有的话munmap掉。

如果有data_fd或post_data_fd,那么关掉它;

将请求关联的所有cgi的环境变量env释放掉;

将各种动态申请的字符串释放掉;

不明白接下来这个if什么目的,代码也没有注释:

        如果 ((req->keepalive == KA_ACTIVE)  &&  (req->response_status < 500) && req->kacount > 0) 将进行如下操作:新分配一个request结构conn,将各种信息从req里复制到新配分的conn里。将req结构放到free链表里,返回。

然后对一种遗留问题进行处理:CERN服务器要求POST请求最后加一行额外的CRLF,这个不算在content-length里。如果请求类型是M_POST,那么试图读取掉剩余的信息。

最后关闭连接,将req结构踢加入free链表。



总结

至此,只剩下process_select()调用的:

read_header(current);

read_body(current);

write_body(current);

process_get(current);

read_from_pipe(current);

write_from_pipe(current);

他们分析HTTP请求,进行相应的处理。

博主目前的复习重点还是在基础c/s模型的掌握上,这些函数就先略过了,为了找工作还有许多别的东西要学要复习。


boa的整体流程基本了解了。总结一下:

主要数据结构:

每个连接分配一个reqeust结构体,里边包含着连接的各种上下文信息,由于数据成员很多,这里只列举部分:

  1. struct request {                /* pending requests */  
  2.     int fd;                     /* client's socket fd */  
  3.     int status;                 /* see #defines.h */  
  4.     time_t time_last;           /* time of last succ. op. */  
  5.     char *pathname;             /* pathname of requested file */  
  6.     int simple;                 /* simple request? */  
  7.     int keepalive;              /* keepalive status */  
  8.     int kacount;                /* keepalive count */  
  9.   
  10.     int data_fd;                /* fd of data */  
  11.     unsigned long filesize;     /* filesize */  
  12.     unsigned long filepos;      /* position in file */  
  13.     char *data_mem;             /* mmapped/malloced char array */  
  14.     int method;                 /* M_GET, M_POST, etc. */  
  15.   
  16.     char *logline;              /* line to log file */  
  17.     char *header_line;          /* beginning of un or incompletely processed header line */  
  18.     int client_stream_pos;      /* how much have we read... */  
  19.   
  20.     int buffer_start;           /* where the buffer starts */  
  21.     int buffer_end;             /* where the buffer ends */  
  22.   
  23.     char local_ip_addr[NI_MAXHOST]; /* for virtualhost */  
  24.   
  25.     /* CGI vars */  
  26.     int remote_port;            /* could be used for ident */  
  27.   
  28.     char remote_ip_addr[NI_MAXHOST]; /* after inet_ntoa */  
  29.   
  30.     int is_cgi;                 /* true if CGI/NPH */  
  31.     int cgi_status;  
  32.     int cgi_env_index;          /* index into array */  
  33.   
  34.     /* Agent and referer for logfiles */  
  35.     char *header_user_agent;  
  36.     char *header_referer;  
  37.   
  38.     int post_data_fd;           /* fd for post data tmpfile */  
  39.     char *content_type;         /* env variable */  
  40.     char *content_length;       /* env variable */  
  41.   
  42.     struct mmap_entry *mmap_entry_var;  
  43.   
  44.     struct request *next;       /* next */  
  45.     struct request *prev;       /* previous */  
  46.   
  47.     /* everything below this line is kept regardless */  
  48.     char buffer[BUFFER_SIZE + 1]; /* generic I/O buffer */  
  49.     char client_stream[CLIENT_STREAM_SIZE]; /* data from client - fit or be hosed */  
  50.     char *cgi_env[CGI_ENV_MAX + 4];             /* CGI environment */  
  51.   
  52. };  


三个链表:

extern request *request_ready;  /* first in ready list */

extern request *request_block;  /* first in blocked list */

extern request *request_free;   /* first in free list */

boa里,对请求的操作是以这三个链表为中心,而不是以select为驱动。这不同于《Unix网络编程》上的简单例子,一般都是select为主。在这个程序里,是以对前两个链表的扫描、更新为主要任务。free链表用来减少malloc和free request结构体的次数。

两个fdset:
extern fd_set block_read_fdset; /* fds blocked on read */
extern fd_set block_write_fdset; /* fds blocked on write */
这两个fdset用作select参数,在process_requests()和fdset_update()里,会对这两个fdset进行设置。



主要算法流程:

获取参数

读配置文件

打开日志

创建server_s

建立信号处理机制

成为daemon进程

select循环

        检查各种信号是否发生。

        将阻塞队列的请求更新到就绪队列。

        处理就绪队列的请求,并进行相应处理。block_read_fdset和block_write_fdset的更新在这两个函数里进行。

        设置server_s,并调用select

        如果有新连接,置pending_requests为1,延时到fdset_update或process_requests里处理。




boa代码分析暂告一段落,以后如果别的事情做的差不多了,而且时间足够,应该会继续看完。


fighting~   :)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多