BOA代码笔记 6


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:
                /* a non-status that will terminate the request */
                retval = req_flush(current);
                /*
                 * retval can be -2=error, -1=blocked, or bytes left
                 */
                if (retval == -2) { /* error */
                    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);

        /* we put this here instead of after the switch so that
         * if we are on the last request, and get_request is successful,
         * current->next is valid!
         */
        if (pending_requests)
            get_request(server_s);

        switch (retval) {
        case -1:               /* request blocked */
            trailer = current;
            current = current->next;
            block_request(trailer);
            break;
        case 0:                /* request complete */
            current->time_last = current_time;
            trailer = current;
            current = current->next;
            free_request(&request_ready, trailer);
            break;
        case 1:                /* more to do */
            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 {                /* pending requests */
    int fd;                     /* client's socket fd */
    int status;                 /* see #defines.h */
    time_t time_last;           /* time of last succ. op. */
    char *pathname;             /* pathname of requested file */
    int simple;                 /* simple request? */
    int keepalive;              /* keepalive status */
    int kacount;                /* keepalive count */

    int data_fd;                /* fd of data */
    unsigned long filesize;     /* filesize */
    unsigned long filepos;      /* position in file */
    char *data_mem;             /* mmapped/malloced char array */
    int method;                 /* M_GET, M_POST, etc. */

    char *logline;              /* line to log file */
    char *header_line;          /* beginning of un or incompletely processed header line */
    int client_stream_pos;      /* how much have we read... */

    int buffer_start;           /* where the buffer starts */
    int buffer_end;             /* where the buffer ends */

    char local_ip_addr[NI_MAXHOST]; /* for virtualhost */

    /* CGI vars */
    int remote_port;            /* could be used for ident */

    char remote_ip_addr[NI_MAXHOST]; /* after inet_ntoa */

    int is_cgi;                 /* true if CGI/NPH */
    int cgi_status;
    int cgi_env_index;          /* index into array */

    /* Agent and referer for logfiles */
    char *header_user_agent;
    char *header_referer;

    int post_data_fd;           /* fd for post data tmpfile */
    char *content_type;         /* env variable */
    char *content_length;       /* env variable */

    struct mmap_entry *mmap_entry_var;

    struct request *next;       /* next */
    struct request *prev;       /* previous */

    /* everything below this line is kept regardless */
    char buffer[BUFFER_SIZE + 1]; /* generic I/O buffer */
    char client_stream[CLIENT_STREAM_SIZE]; /* data from client - fit or be hosed */
    char *cgi_env[CGI_ENV_MAX + 4];             /* CGI environment */

};


三个链表:

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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值