process_requests()
昨天分析到了switch部分。
process_requests()还剩下的,也就是处理请求的switch部分和处理retval的swtich部分。先看下剩下这部分的源码:
- switch (current->status) {
- case READ_HEADER:
- case ONE_CR:
- case ONE_LF:
- case TWO_CR:
- retval = read_header(current);
- break;
- case BODY_READ:
- retval = read_body(current);
- break;
- case BODY_WRITE:
- retval = write_body(current);
- break;
- case WRITE:
- retval = process_get(current);
- break;
- case PIPE_READ:
- retval = read_from_pipe(current);
- break;
- case PIPE_WRITE:
- retval = write_from_pipe(current);
- break;
- case DONE:
-
- retval = req_flush(current);
-
-
-
- if (retval == -2) {
- current->status = DEAD;
- retval = 0;
- } else if (retval > 0) {
- retval = 1;
- }
- break;
- case DEAD:
- retval = 0;
- current->buffer_end = 0;
- SQUASH_KA(current);
- break;
- default:
- retval = 0;
- fprintf(stderr, "Unknown status (%d), "
- "closing!\n", current->status);
- current->status = DEAD;
- break;
- }
-
- }
-
- if (sigterm_flag)
- SQUASH_KA(current);
-
-
-
-
-
- if (pending_requests)
- get_request(server_s);
-
- switch (retval) {
- case -1:
- trailer = current;
- current = current->next;
- block_request(trailer);
- break;
- case 0:
- current->time_last = current_time;
- trailer = current;
- current = current->next;
- free_request(&request_ready, trailer);
- break;
- case 1:
- current->time_last = current_time;
- current = current->next;
- break;
- default:
- log_error_time();
- fprintf(stderr, "Unknown retval in process.c - "
- "Status: %d, retval: %d\n", current->status, retval);
- current = current->next;
- break;
- }
如果状态是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结构体,里边包含着连接的各种上下文信息,由于数据成员很多,这里只列举部分:
- struct request {
- int fd;
- int status;
- time_t time_last;
- char *pathname;
- int simple;
- int keepalive;
- int kacount;
-
- int data_fd;
- unsigned long filesize;
- unsigned long filepos;
- char *data_mem;
- int method;
-
- char *logline;
- char *header_line;
- int client_stream_pos;
-
- int buffer_start;
- int buffer_end;
-
- char local_ip_addr[NI_MAXHOST];
-
-
- int remote_port;
-
- char remote_ip_addr[NI_MAXHOST];
-
- int is_cgi;
- int cgi_status;
- int cgi_env_index;
-
-
- char *header_user_agent;
- char *header_referer;
-
- int post_data_fd;
- char *content_type;
- char *content_length;
-
- struct mmap_entry *mmap_entry_var;
-
- struct request *next;
- struct request *prev;
-
-
- char buffer[BUFFER_SIZE + 1];
- char client_stream[CLIENT_STREAM_SIZE];
- char *cgi_env[CGI_ENV_MAX + 4];
-
- };
三个链表:
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~ :)
|