Boa Source Code Analysis (3)-code structure

Source: Internet
Author: User
1 boa. C main program: ---- 1) close the file for (I = 3; I <= 1024; I ++) Close (I); ---- 2) set the process permission mask umask (~ 0600); RW----; ---- 3) Open the black hole and point the standard input and output to it. Open ("/dev/null", 0); dup2 (devnullfd, stdin_fileno); dup2 (devnullfd, stdout_fileno); ---- 4) Update the timestamp, which is required for logs. Time (effect _ time); ---- 5) parse the command line-fserver_root = strdup (optaarg);-r chdir (optarg); chroot (optarg ); chdir ("/");-D do_fork = 0; ---- 6) // ensure that the root directory of the server is valid. Fixup_server_root (); ---- 7) // read the configuration file and use yyparse to ensure that necessary variable settings are correct. Read_config_files (); ---- 8) // open access log, error log, [CGI log]; and set close-on-exec to true, that is, close the file descriptor after exec is called. Open_logs (); ---- 9) // create a TCP socket, set it to nonblock, and set close-on-exec to true. In this way, CGI cannot be written to it when exec is called... Server_s = create_server_socket (); // The address reuse function is enabled. For details, see UNIX network programming. --- 10) // specify handleinit_signals (); --- 11) // set the user ID and process group ID. Drop_privs (); // drop privilege --- 12) set up the environment variables that are common to all CGI scripts create_common_env (); --- 13) Fork sub-process, parent process exited. Then the sub-process becomes the daemon if (do_fork) Switch (Fork () --- 14) and obtains the PID, which is used to generate a unique temporary file name or path. Int pid = getpid (); --- 15) Update the timestamp and enter the main loop. Timestamp (); select_loop (server_s) {1) Clear, block_read_fdset, block_write_fdset; 2) Set server_s and request timeout. 3) enter while (1) {1) to process signals such as sighup, sigchld, sigalrm, and sigterm. 2) reset max_fd =-1; 3) Move the appropriate request from the block linked list to the ready linked list. If (reques_block) fdset_update (); // 4) process_requests (server_s); 5) if (! Sigterm_flag & total_connections <(max_connections-10) boa_fd_set (server_s, & block_read_fdset);/* Server Always set */6) reset timeout 7) Select call, select (max_fd + 1, & block_read_fdset, & block_write_fdset, null, (request_ready | request_block? & Req_timeout: NULL) 8) update current time, time (& curent_time); 9) if (fd_isset (server_s, & block_read_fdset) pending_requests = 1 ;}}

I. Let's take a look at fdset_update ()

There are three request linked lists in Boa.

Request * request_ready = NULL;/* Ready list head */
Request * request_block = NULL;/* blocked list head */
Request * request_free = NULL;/* Free List head */

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 */    char *header_end;           /* last known end of header, or end of processed data */    int parse_pos;              /* how much have we parsed */    int client_stream_pos;      /* how much have we read... */    int buffer_start;           /* where the buffer starts */    int buffer_end;             /* where the buffer ends */    char *http_version;         /* HTTP/?.? of req */    int response_status;        /* R_NOT_FOUND etc. */    char *if_modified_since;    /* If-Modified-Since */    time_t last_modified;       /* Last-modified: */    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 *path_info;            /* env variable */    char *path_translated;      /* env variable */    char *script_name;          /* env variable */    char *query_string;         /* env variable */    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 request_uri[MAX_HEADER_LENGTH + 1]; /* uri */    char client_stream[CLIENT_STREAM_SIZE]; /* data from client - fit or be hosed */    char *cgi_env[CGI_ENV_MAX + 4];             /* CGI environment */#ifdef ACCEPT_ON    char accept[MAX_ACCEPT_LENGTH]; /* Accept: fields */#endif};typedef struct request request;

 

static void fdset_update(void){request *current, *next;for (current = request_block; current; current = next){time_t time_since = current_time - current->time_last;next = current->next;/* hmm, what if we are in "the middle" of a request and not * just waiting for a new one... perhaps check to see if anything * has been read via header position, etc... */if (current->kacount < ka_max && /* we *are* in a keepalive */(time_since >= ka_timeout) && /* ka timeout */!current->logline) /* haven't read anything yet */current->status = DEAD; /* connection keepalive timed out */else if (time_since > REQUEST_TIMEOUT){log_error_doc(current);fputs("connection timed out\n", stderr);current->status = DEAD;}if (current->buffer_end && current->status < DEAD){if (FD_ISSET(current->fd, &block_write_fdset))ready_request(current);else{BOA_FD_SET(current->fd, &block_write_fdset);}} else{switch (current->status){case WRITE:case PIPE_WRITE:if (FD_ISSET(current->fd, &block_write_fdset))ready_request(current);else{BOA_FD_SET(current->fd, &block_write_fdset);}break;case BODY_WRITE:if (FD_ISSET(current->post_data_fd, &block_write_fdset))ready_request(current);else{BOA_FD_SET(current->post_data_fd, &block_write_fdset);}break;case PIPE_READ:if (FD_ISSET(current->data_fd, &block_read_fdset))ready_request(current);else{BOA_FD_SET(current->data_fd, &block_read_fdset);}break;case DONE:if (FD_ISSET(current->fd, &block_write_fdset))ready_request(current);else{BOA_FD_SET(current->fd, &block_write_fdset);}break;case DEAD:ready_request(current);break;default:if (FD_ISSET(current->fd, &block_read_fdset))ready_request(current);else{BOA_FD_SET(current->fd, &block_read_fdset);}break;}}current = next;}}

In the for loop,

First, obtain time_since as the time from the previous successful operation.

If the request is out of keepalive, time_since is greater than ka_timeout (which can be configured in the configuration file) and nothing is read, The Request status changes to dead.

If time_since is greater than request_timeout (60), the status changes to dead.

If the buffer zone has data and the status is smaller than dead:

If it is not in block_write_fdset, put it in block_write_fdset.

If FD is already in block_write_fdset, call ready_request to transfer the request from the block queue to the ready queue, and clear the flag in block_write_fdset.

The ready_request function clears the corresponding FD from the fdset according to the status.

Other cases:

If the status is write, pipe_write, or done, put the request in block_write_fdset. If it is already in use, call ready_request.

The status is body_write, And the post_data_fd of the request is processed above. Post_data_fd:/* FD for post data tmpfile */. It should be a temporary file in the client POST method.

The status is pipe_read. Similar processing is performed on data_fd of the request, but block_read_fdset is checked.

If the status is dead, you can directly call ready_request.

Otherwise, check whether the FD is in block_read_fdset and handle it accordingly.

 

2. Check the process_erquests function.

void process_requests(int server_s){    int retval = 0;    request *current, *trailer;    if (pending_requests) {        get_request(server_s);#ifdef ORIGINAL_BEHAVIOR        pending_requests = 0;#endif    }    current = request_ready;    while (current) {        time(¤t_time);        if (current->buffer_end && /* there is data in the buffer */            current->status != DEAD && current->status != DONE) {            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) {                /* notice the >= which is different from below?                   Here, we may just be flushing headers.                   We don't want to return 0 because we are not DONE                   or DEAD */                retval = 1;            }        } else {            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;        }    }}

 

For request traversal in each ready queue, the returned value-1 indicates that the request needs to enter the block queue; the returned value 0 indicates that the request ends; the returned value 1 indicates that the request is still in the ready queue and needs further processing.

First, check whether pending_requests exists. If get_request (server_s) is called, accept a connection and add ready_queue.

Get_request (server_s); the general function is to accept a request, perform some simple initialization, and add ready_queue.

Then round-robin The ready linked list:

If there is data to be written and the status is not dead or done, call req_flush (current ).

Check whether there is pending_requests in the last round. If yes, add ready_queue.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.