CVS diff for request.c between 1.15 and 1.11:

Revision 1.11 Revision 1.15
Line 8 Line 8
 
#define MIN(a,b) (a <= b ? a : b) 
 
 
#define MIN(a,b) (a <= b ? a : b) 
 
void simple_parse_header(msg*);
int simple_parse_request(msg*);
int simple_parse_request_line(msg*);
 
 
/**** 
* read_request() 
/**** 
* read_request() 
Line 23 Line 19
 *            and buffers to read into. 
* @return 0 or 1 on success, -1 on failure 
****/ 
 *            and buffers to read into. 
* @return 0 or 1 on success, -1 on failure 
****/ 
int read_request(msg * req) { 
int read_request(msg* req) { 
    int ret_val; 
    int ret_val; 
    /* initialize fields in msg* req */
    req->simple = 1;                    /* Assume HTTP/0.9 by default */
    /* Initialize connection settings */
    req->ka_status = KA_ACTIVE;         /* by default by http/1.1 */ 
    req->status = START_LINE;
    req->is_cgi = 0;

    /*
     * Keep-alive should be on by default for HTTP/1.1, but it doesn't
     * work yet so I'm disabling it until I've had time to work on it.
     */
    req->ka_status = KA_INACTIVE;       /* by default by http/1.1 */
 
    req->ka_count = ka_max_con - 1;     /* Limit keep-alive req's remaining */ 
req->ka_timeout = ka_max_dur + time(NULL); 
    req->ka_count = ka_max_con - 1;     /* Limit keep-alive req's remaining */ 
req->ka_timeout = ka_max_dur + time(NULL); 
    /* additional initialization */
    req->p_buf_left = MAX_BUF_SIZE - 1; // off 1 to put '\0'
    req->p_buf_head = req->p_buf;
    req->p_eof = 0;
    req->p_buf_read = 0;
    req->p_entity_body = NULL;

    /* Read the first "chunk" of the first request on this connection */
    ret_val = read_request_chunk(req);

    for (;;)                    // keep-alive loop
    {
        /* If a socket error occurs, just close the connection */ 
    /* Keep-alive loop */
    for (;;) {
        pth_event_t ev_read;
        
        /* Set per-request settings */
        req->simple = 1;                    /* Assume HTTP/0.9 by default */
        req->status = START_LINE;
        req->is_cgi = 0;
        req->p_buf_left = MAX_BUF_SIZE - 1; // off 1 to put '\0'
        req->p_buf_head = req->p_buf;
        req->p_eof = 0;
        req->p_buf_read = 0;
        req->p_entity_body = NULL;
 
        /* Wait the "keep-alive" timeout even for new connections */
        ev_read = pth_event(PTH_EVENT_TIME,
                            pth_timeout(MIN(cow_rto_s, ka_max_dur), 0));
        ret_val = cow_read_line(req->fdp, req->p_buf, MAX_BUF_SIZE, ev_read);
        pth_event_free(ev_read, PTH_FREE_THIS);

        /* If a socket error/timeout occurs, just close the connection */
        if (ret_val < 0) {
        if (ret_val < 0) {
            COW_CLOSE(req->fdp); 
            cow_close_socket(req->fdp); 
            --cow_cur_conn; 
return (ret_val); 
}
            --cow_cur_conn; 
return (ret_val); 
}
        /* Did we reach the end of the headers? */
        if (1 == ret_val) {
            // we can use simple_parse_request
            ret_val = simple_parse_request(req);
            if (0 > ret_val)    // error
            {
                // bad header. appropriate response has been sent.
                COW_CLOSE(req->fdp);
                --cow_cur_conn;
                return -1; 
        //printf("DEBUG: read line '%s' with retval %d, %d remains (%s)\n", req->p_buf, ret_val, req->fdp->incnt, req->fdp->inptr);
        
        /* Parse the first line of the request */
        ret_val = parse_request_line(req);
        if (ret_val < 0) {
            cow_close_socket(req->fdp);
            return ret_val;
        }

        /* Parse all header lines */
        for (;;) {
 
            ev_read = pth_event(PTH_EVENT_TIME,
                                pth_timeout(cow_rto_s, cow_rto_u));
            int len = cow_read_line(req->fdp, req->p_buf, MAX_BUF_SIZE, ev_read);
            pth_event_free(ev_read, PTH_FREE_THIS);
            if (len < 0) {
                /* Error; close connection */
                cow_close_socket(req->fdp);
                return len;
            } else if (len == 0) {
                /* Blank header line --> end of headers */
                break;
            }
            }
        } else {
            /*
             * The request data we've seen so far did not end in \r\n\r\n or
             * \n\n, which means one of
             * 1. Read header not completed.
             * 2. The request includes a body (e.g. POST method) and we've
             *    already passed the end of the headers and started reading
             *    the body.
             * We'll have to use the generic (slower) parse_request routine
             * which contains extra logic to read more chunks as needed.
             */
            ret_val = parse_request(req);
            if (0 != ret_val) {
                COW_CLOSE(req->fdp);
                --cow_cur_conn;
                return -1;
            }
        }

        if ((KA_ACTIVE != req->ka_status) || (1 > --req->ka_count)) {
            COW_CLOSE(req->fdp); 
            //printf("DEBUG: read header '%s' with ret val %d, %d remains (%s)\n", req->p_buf, len, req->fdp->incnt, req->fdp->inptr);

            /* Parse the header */
            parse_header(req);
        }

        /* Done reading/parsing headers.  Actually do the request now */
        cow_tot_parsed++;
        switch (req->p_method) {
            case M_HEAD:
            case M_GET:
                ret_val = response_get(req);
                break;
            case M_POST:                // not yet
                STATIC_RESPONSE(req->fdp, 501);
                ret_val = -1;
                break;
#ifdef ENABLE_PUT
            case M_PUT:
                PRINT("method is put....\n");
                process_header_end(req);
                ret_val = response_put(req);
 
                PRINT("breaking put\n");
                break;
#endif
            default:
                /* Send back a 503 if we get something we don't recognize */
                PRINT("def...\n");
                STATIC_RESPONSE(req->fdp, 503);
                ret_val = -1;
        }
#ifdef USE_TCPCORK
        /* Flush pending output */
        setsockopt(req->fdp, SOL_TCP, TCP_CORK, &zero, sizeof(zero));
#else
        ap_bflush(req->fdp);
#endif

        /* Any type of error condition means keep-alive can't continue. */
        if (ret_val < 0) {
            /* Error */
            cow_close_socket(req->fdp);
            --cow_cur_conn;
            --cow_cur_conn;
            return ret_val;
        }

        /* We shouldn't get down here right now since I've disabled keep alive */
        assert(0);

        /* read the [first] portion for the second time or later */
        ret_val = read_request_ka(req);

    }                           // end oF KEEP-ALIVE LOOP
}                               // end of read_request() 
            return -1;
        }

        /*
         * If keep-alive is not active or if we've done our maximum number of
         * requests on this connection, close the connection.
         */
        if ((req->ka_status != KA_ACTIVE) || (--req->ka_count == 0)) {
            cow_close_socket(req->fdp);
            --cow_cur_conn;
            return 0;
 
        }

        /* Loop back and wait for another request on this socket */
    }
}
 
inline int read_request_ka(msg * req) { 
/* timeout */ 
 
inline int read_request_ka(msg * req) { 
/* timeout */ 
Line 122 Line 154
     */ 
ev_read = pth_event(PTH_EVENT_TIME, 
pth_timeout(MIN(cow_rto_s, ka_max_dur), 0)); 
     */ 
ev_read = pth_event(PTH_EVENT_TIME, 
pth_timeout(MIN(cow_rto_s, ka_max_dur), 0)); 
 
#ifdef USE_TCPCORK 
    /* Try to read another request from the socket; -1 here means timeout */ 
req->p_buf_read = 
pth_read_ev(req->fdp, req->p_buf_head, req->p_buf_left, ev_read);
    /* Try to read another request from the socket; -1 here means timeout */ 
req->p_buf_read = 
pth_read_ev(req->fdp, req->p_buf_head, req->p_buf_left, ev_read);
 
#else
    req->p_buf_read =
        ap_bread(req->fdp, req->p_buf_head, req->p_buf_left, ev_read);
#endif 
    pth_event_free(ev_read, PTH_FREE_THIS); 
 
if (0 == req->p_buf_read) { 
    pth_event_free(ev_read, PTH_FREE_THIS); 
 
if (0 == req->p_buf_read) { 
Line 157 Line 193


 


 
/****
 * read_request_chunk()
 *
 * Reads the next chunk of the request.  This function blocks the current
 * thread until at least one more byte of the request can be read (or until
 * EOF is seen) and then returns.  It does not necessarily read the entire
 * request, just "more" of it.  The return value indicates whether the
 * request is complete or not.
 *
 * @param req The request structure containing the file descriptor to read
 *            from and the buffers to read into.
 *
 * @return 1 if the request has been completely read, 0 if it has only been
 *         partially read, and -1 if an error occurred.
 ****/
inline int read_request_chunk(msg* req) {
    /* timeout */
    pth_event_t ev_read;

    /*
     * If there's no room to read any more data or if we've already received
     * EOF, just fail.
     */
    if (req->p_buf_left <= 0 || req->p_eof)
        return -1;

    /*
     * Right now this only supports regular socket timeout.  We should also
     * make it handle keep-alive timeouts.
     */
    ev_read = pth_event(PTH_EVENT_TIME, pth_timeout(cow_rto_s, cow_rto_u));
    req->p_buf_read =
        pth_read_ev(req->fdp, req->p_buf_head, req->p_buf_left, ev_read) ;
    pth_event_free(ev_read, PTH_FREE_THIS);

    if (0 > req->p_buf_read) {
        // Read error; timeout or bad connection.
        return -1;
    } else if (0 == req->p_buf_read) {
        /* EOF encountered as only character */
        req->p_eof = 1;
        return -1;
    }

    /* TODO: Pth was already yielding waiting for input; why yield now? */
    //pth_yield(NULL);

    /*
     * Update request structure to reflect how much of the request has been
     * read.  Also NULL terminate the data we've read so far.
     */
    req->p_buf_left -= req->p_buf_read;
    req->p_buf_head += req->p_buf_read;
    //req->p_buf_head = (char *)(&req->p_buf_head[req->p_buf_read]);
    req->p_buf_head[0] = '\0';

    /*
     * If the end of the data we've read ends with \r\n\r\n or \n\n then we're
     * done reading the request.
     */
    if (strcmp((req->p_buf_head-4), "\r\n\r\n") == 0 ||
        strcmp((req->p_buf_head-4), "\n\n") == 0) {
        req->p_header_end = req->p_buf_head;
        return 1;
    } else {
        /* Still haven't seen the end of the request */
        req->p_header_end = NULL;
        return 0;
    }
}

int parse_request(msg* req) {
    /*
       Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
     */

    int retval = 0;

    if (START_LINE == req->status)      // the first line
        retval = parse_request_line(req);
    if (retval)
        return retval;

    // parse header lines
    if (!req->simple) {
        retval = parse_header(req);
        if (retval)
            return retval;
    }

    switch (req->p_method) {
    case M_HEAD:
    case M_GET:
        response_get(req);
        break;
    case M_POST:                // not yet
        PRINT("method is post\n");
        response_post(req);
        PRINT("breaking post\n");
    case M_PUT:
        PRINT("method is put....\n");
        process_header_end(req);
        response_put(req);
        PRINT("breaking put\n");
        break;
    default:
        /* Send back a 503 if we get something we don't recognize */
        PRINT("def...\n");
        STATIC_RESPONSE(req->fdp, 503);
        return -1;
    }

    return 0;
} 
 
 
/**** 
* parse_request_line() 

* Parses the first line of an HTTP request. The request type, URI and 
* protocol version information is all stored in the provided request
 
/**** 
* parse_request_line() 

* Parses the first line of an HTTP request. The request type, URI and 
* protocol version information is all stored in the provided request
 * structure.  This function may need to call read_request_chunk() to get
 * structure. 
 * more of the request from the network if it is not already all available.
 

* @param req The HTTP request structure which contains the request buffers 
* and which the request information should be stored to. 

* @return 0 on success, -1 on failure 
****/

* @param req The HTTP request structure which contains the request buffers 
* and which the request information should be stored to. 

* @return 0 on success, -1 on failure 
****/
int parse_request_line(msg * req) { 
int parse_request_line(msg* req) { 
    /* BNF: 

* Request-Line = Method SP Request-URI SP HTTP-Version CRLF 
    /* BNF: 

* Request-Line = Method SP Request-URI SP HTTP-Version CRLF 
Line 309 Line 230
        uri = logline + 4; 
} else { 
// bad request or not implemented 
        uri = logline + 4; 
} else { 
// bad request or not implemented 
 
        //printf("DEBUG: Service = '%s'\n", logline); 
        STATIC_RESPONSE(req->fdp, 501); 
return -1; 

 
/* Scan to start of non-whitespace (i.e. the URI) */
        STATIC_RESPONSE(req->fdp, 501); 
return -1; 

 
/* Scan to start of non-whitespace (i.e. the URI) */
    while (*(++uri) == ' ') {
        /*
    while (*(++uri) == ' ')
        ; 
         * Make sure we don't scan off the end of the input in our attempt to
         * find non-whitespace.
         */
        if (uri > req->p_buf_head) {
            /*
             * End of input; have we filled up the entire buffer or have we
             * just not received the entire request from the network yet?
             */
            if (0 >= req->p_buf_left || req->p_header_end)
                /*
                 * Buffer is full or the request is finished; we can't read 
                 * any more from the network.  Fail now.
                 */
                return -1;

            /*
             * There's still room to read more into the buffer.  Move our uri
             * pointer back one character and try to read some more.
             */
            --uri;
            if (0 > read_request_chunk(req))
                return -1;
        }
    }
 
 
/* scan to end of URI */ 
stop = uri; 
for (;;) {
 
/* scan to end of URI */ 
stop = uri; 
for (;;) {
        /* Make sure we don't run past the end of the current input */
        if (stop >= req->p_buf_head) {
            /* Try to read some more from the socket */
            if (read_request_chunk(req) < 0)
                return -1;
        /*
         * End of URI will be a space (if we're using HTTP/1.0 or above) or
         * the null-terminator (if we got an HTTP/0.9 simple request).
         */
        if (*stop == '\0' || *stop == ' ') 
            continue;
        }

        /* Space or \n indicates end of the URI */
        if (*stop == ' ')
 
            break; 
 
/*
            break; 
 
/*
         * \r should mean end of URI (if it's followed by \n); if it
         * is followed by something else, it's an error. 
         * \r should mean end of URI and be followed by \n, but since
         * cow_read_line already stripped trailing \r\n's, this must be
 
         * a stray (and an error).
         */ 
if (*stop == '\r') {
         */ 
if (*stop == '\r') {
            /* Make sure we have one more character */
            if (stop+1 >= req->p_buf_head)
            STATIC_RESPONSE(req->fdp, 400);
            return -1; 
                read_request_chunk(req);

            if (*(stop+1) == '\n') {
                break;
            } else {
                /* Stray \r; bad request */
                STATIC_RESPONSE(req->fdp, 400);
                return -1;
            }
 

 
/* Not there yet... */ 

 
/* Not there yet... */ 
Line 398 Line 283
            req->hv_major = p1; 
req->hv_minor = p2; 
req->simple = 0; 
            req->hv_major = p1; 
req->hv_minor = p2; 
req->simple = 0; 
            if (p1 == 0 || p1+p2 < 2) 
            if (p1 == 0 || p1+p2 < 2) { 
                req->ka_status = KA_INACTIVE;
                req->ka_status = KA_INACTIVE;
 
        } else { 
#ifdef LOGGING 
fprintf(ELOG, "Malformed request '%s %s'\n", 
        } else { 
#ifdef LOGGING 
fprintf(ELOG, "Malformed request '%s %s'\n", 
Line 414 Line 300
        req->ka_status = KA_INACTIVE; 

 
        req->ka_status = KA_INACTIVE; 

 
    /*
    return 0; 
     * Scan until we reach a \n; at that point we'll know for sure
     * that the request line can be processed.
     */
    for (;;) {
        if (*stop == '\0') {
            /* Scanned past end of input; try to read some more */
            if (0 > read_request_chunk(req))
                return -1;
        }

        /*
         * If we reach a \n or a \r\n, then we know we've read enough to
         * process the request line.  Then we can return to the user.
         */
        if (*stop == '\n') {
            req->status = ONE_CRLF;
            req->p_pos = stop + 1;
            return 0;
        }

        ++stop;
    }

    /* unreachable */
    assert(0);
    return -1;                  /* error */
 

 
/* 

 
/* 
Line 527 Line 387
    char *line; 
int check_CRLF; 
 
    char *line; 
int check_CRLF; 
 
    req->header_line = 1 + req->p_pos;
 
 
    for (;;) {
    for (;;) {
        line = req->header_line;
        line = req->p_buf; 
        /*
         * At this point, one \r\n (or \n) was just parsed so another immediate
         * \r\n (or \n) means we got the end of the header.
         */
        if (('\r' == line[0]) && ('\n' == line[1])) {
            req->p_entity_body = &line[2];
            req->header_line = &line[2];
            return 0;
        } else if ('\n' == line[0]) {
            req->p_entity_body = &line[1];
            req->header_line = &line[1];
            break;
        }
 
 
/* 
* Look for the ":" between the header name and value. If we don't 
 
/* 
* Look for the ":" between the header name and value. If we don't 
Line 552 Line 396
         * one. 
*/ 
value = strchr(line, ':'); 
         * one. 
*/ 
value = strchr(line, ':'); 
        if (NULL == value) {
            /*
             * No name/value separator found.  Jump to the end of this line.
        if (NULL == value)
            return 0;
         
             * If we have not yet read enough from the network to find the
             * end of the line, keep reading until we have it or until we
             * detect a socket error.
             */
            while (NULL == (req->header_line = strchr(line, '\n'))) {
                int retval = read_request_chunk(req);
                if (retval < 0)
                    return -1;
            }
            req->header_line++;
        }
 
        /* 
* Figure out which header this is; it looks like this code may have 
* been taken from Boa. 
*/ 
*value++ = '\0'; 
to_upper(line); // header type is case insensitive
        /* 
* Figure out which header this is; it looks like this code may have 
* been taken from Boa. 
*/ 
*value++ = '\0'; 
to_upper(line); // header type is case insensitive
        // while((c=*value)&&(c==' '||c=='\t')) // original 

                         
 
        /* Find the start of the value */
        for (;;) { 
int retval; 
c = *value; 
 
if ('\0' == c) {
        for (;;) { 
int retval; 
c = *value; 
 
if ('\0' == c) {
                /*
                 * We should only encounter a NULL character if we haven't read
                /* Empty header value */
                return 0; 
                 * enough of the request yet.  Read some more of the request and
                 * then continue the loop.  If we're out of buffer space, fail
                 * now.
                 */
                if (0 >= req->p_buf_left || req->p_header_end)
                    return -1;
                retval = read_request_chunk(req);
                if (retval < 0)
                    return -1;
                continue;
 
            } else if (c == ' ' || c == '\t') { 
/* Ignore whitespace */ 
value++; 
            } else if (c == ' ' || c == '\t') { 
/* Ignore whitespace */ 
value++; 
Line 600 Line 423

 
if (!memcmp(line, "HOST", 5)) { 

 
if (!memcmp(line, "HOST", 5)) { 
            //not yet 
            // TODO 
 
} else if (!memcmp(line, "IF_MODIFIED_SINCE", 18) 
&& !req->if_modified_since) {
 
} else if (!memcmp(line, "IF_MODIFIED_SINCE", 18) 
&& !req->if_modified_since) {
            req->if_modified_since = value;
        }

        else if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type) {
            // TODO
            
        } else if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type) {
            // TODO 
            req->content_type = value;
 
 
} else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length) {
 
} else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length) {
            req->content_length = value;

        } else if (!memcmp(line, "CONNECTION", 11)
                 // && ka_max 
                 // && req->keepalive != KA_STOPPED)
            // TODO

        } else if (!memcmp(line, "CONNECTION", 11) && 
                    ka_max_con && req->ka_status != KA_STOPPED) {
            if (0 == memcmp(value, "close", 5)) { 
                 && ka_max_con && req->ka_status != KA_STOPPED) {
            /* Skip whitespace */
            while (*value == ' ')
                value++;
            if (0 == memcmp(value, "close", 5))
 
                req->ka_status = KA_INACTIVE;
                req->ka_status = KA_INACTIVE;
            // TODO: Fix keepalive (deal with other "connection" headers) 
            }
 
            // TODO: deal with other "connection" headers
 
} else if (!memcmp(line, "REFERER", 8)) {
 
} else if (!memcmp(line, "REFERER", 8)) {
            req->header_referer = value; 
#ifdef LOGGING
 
            // TODO
#endif
 
} else if (!memcmp (line, "ACCEPT", 7)) { 
// TODO: Honor Accept: headers 
 
} else if (!memcmp(line, "USER_AGENT", 11)) {
 
} else if (!memcmp (line, "ACCEPT", 7)) { 
// TODO: Honor Accept: headers 
 
} else if (!memcmp(line, "USER_AGENT", 11)) {
 
#ifdef LOGGING 
            req->header_user_agent = value;
            req->header_user_agent = value;
        /****** What is this doing here?
#endif
        } 
        } else if (req->is_cgi) {
            // init value of is_cgi is 0.
            // not yet implemented
            check_CRLF = 0;
         ******/

        }

        /*
         * Jump to end of this header and set parsing point to the
         * beginning of the next header.  If we can't find the '\n', it
         * means we haven't read enough of the request from the network yet
         * and we'll have to keep reading chunks until we find the end of
         * this line (or until an error occurs).
         */
        while (NULL == (req->header_line = strchr(line, '\n'))) {
            int retval = read_request_chunk(req);
            if (retval < 0)
                return -1;
        }
        req->header_line++;
 




/*** simple_parse_*() ***/

int simple_parse_request(msg * req) {

/*
 * HTTP_request_message := request_line request_header
 *
 * request_line here means the "start-line"
 * request_header means "(message-header CRLF)"
 */

    int retval = 0;

    if ((retval = simple_parse_request_line(req)))
        return retval;

    /* Parse all header lines */
    simple_parse_header(req);

    switch (req->p_method) {
    case M_HEAD:
    case M_GET:
// for now, return here.
// fix here -> the caller read_request() will handle... or
// is there any better way?
// return 0;
//printf("(before %d)\n", (int)pth_self());
        response_get(req);
//printf("(after  %d)\n", (int)pth_self());
        break;
    /* post not yet handled. it's put for now... */
    /* diff btwn POST and PUT:
       POST = pass (usu. cgi) the entity body to the uri
       PUT  = server just put the entity body to the uri
     */
    case M_POST:
        PRINT("method is post\n");
        response_post(req);
        PRINT("breaking post\n");
    case M_PUT:
        PRINT("method is put\n");
        process_header_end(req);
        response_put(req);
        PRINT("breaking put\n");
        break;
    default:
        /* Send back a 503 if we get something we don't recognize */
        PRINT("def...\n");
        STATIC_RESPONSE(req->fdp, 503);
        return -1;
    }
    return 0;
}

int simple_parse_request_line(msg* req) {
    /*
     *    Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
     */

    char *uri, *stop;
    char *logline = req->p_buf;

    /* Method */
    if (!memcmp(logline, "GET ", 4)) {
        req->p_method = M_GET;
        uri = logline + 3;
    } else if (!memcmp(logline, "PUT ", 4)) {
        req->p_method = M_PUT;
        uri = logline + 3;
    } else if (!memcmp(logline, "HEAD ", 5)) {
        req->p_method = M_HEAD;
        uri = logline + 4;
    } else if (!memcmp(logline, "POST ", 5)) {
        req->p_method = M_POST;
        uri = logline + 4;
    } else {
        // bad request or not implemented
        STATIC_RESPONSE(req->fdp, 501);
        return -1;
    }

    /* Scan to start of non-whitespace (i.e. the URI) */
    while (*(++uri) == ' ') {
        /*
         * Make sure we don't scan off the end of the input in our attempt to
         * find non-whitespace.
         */
        if (uri >= req->p_buf_head)
            return -1;
    }

    /* Scan to end of non-whitespace (i.e. the end of the URI) */
    stop = uri;
    while (*stop != '\0' && *stop != ' ' && *stop != '\n' && *stop != '\r') {
        ++stop;
        /* Make sure URI doesn't overflow buffer */
        if (stop > req->p_buf_head)
            return -1;
    }

    /* If the URI is too long, return an error page */
    if (stop - uri > MAX_REQ_URI_LEN) {
        STATIC_RESPONSE(req->fdp, 414);
        return -1;
    }

    req->p_uri_len = stop - uri;
    memcpy(req->p_uri, uri, req->p_uri_len);
    req->p_uri[req->p_uri_len] = '\0';

    /* HTTP-Version */
    if (*stop == ' ') {
        /* if found, we should get an HTTP/x.x */
        int p1, p2;

        if (sscanf(++stop, "HTTP/%d.%d", &p1, &p2) == 2 && p1 >= 1) {
            req->hv_major = p1;
            req->hv_minor = p2;
            req->simple = 0;
            if (0 == p1 || 0 == p2)
                req->ka_status = KA_INACTIVE;
        } else {
#ifdef LOGGING
            fprintf(ELOG, "Malformed request '%s %s'\n",
                    req->p_uri, stop);
#endif
            STATIC_RESPONSE(req->fdp, 400);
            return -1;
        }
    } else {
        /* No HTTP/x.y present; use HTTP/0.9 */
        req->simple = 1;
        req->ka_status = KA_INACTIVE;
    }

    /*
     * Move the cursor to the end of the request line (identified by either \n
     * or \r\n).
     */
    while (*(++stop) != '\0') {
        if (stop >= req->p_buf_head)
            return -1;
        if ('\n' == *stop) {
            req->status = ONE_CRLF;
            req->p_pos = stop;
            return 0;
        } else if ('\r' == *stop && '\n' == *(1 + stop)) {
            req->status = ONE_CRLF;
            req->p_pos = stop + 1;
            return 0;
        }
    }

    /*
     * This function is only called from simple_parse_request(), which is only
     * called when we know \r\n\r\n or \n\n were found.  Therefore, we should
     * never get down here since we're guaranteed of reaching the end of the
     * requestline.
     */
    assert(0);
    return -1;
}

/*
 * Name: process_header_end
 *
 * Description: takes a request and performs some final checking before
 * init_cgi or init_get
 * Returns -1 for error or NPH;  0 for success
 */


/****
 * simple_parse_header()
 *
 * Parses all request headers after the main request line.  This function is
 * no longer as picky as it used to be; it accepts \n as a valid header end
 * in addition to \r\n and also ignores bogus/malformed headers without
 * raising an error.
 *
 * @param req The request structure containing the buffers to parse.
 ****/
void simple_parse_header(msg* req) {
/*

[70]RFC 2068                        HTTP/1.1                    January 1997


   equivalent to the parameters on a programming language method
   invocation.

          request-header = Accept                   ; Section 14.1
                         | Accept-Charset           ; Section 14.2
                         | Accept-Encoding          ; Section 14.3
                         | Accept-Language          ; Section 14.4
                         | Authorization            ; Section 14.8
                         | From                     ; Section 14.22
                         | Host                     ; Section 14.23
                         | If-Modified-Since        ; Section 14.24
                         | If-Match                 ; Section 14.25
                         | If-None-Match            ; Section 14.26
                         | If-Range                 ; Section 14.27
                         | If-Unmodified-Since      ; Section 14.28
                         | Max-Forwards             ; Section 14.31
                         | Proxy-Authorization      ; Section 14.34
                         | Range                    ; Section 14.36
                         | Referer                  ; Section 14.37
                         | User-Agent               ; Section 14.42

   Request-header field names can be extended reliably only in
   combination with a change in the protocol version. However, new or
   experimental header fields MAY be given the semantics of request-
   header fields if all parties in the communication recognize them to
   be request-header fields.  Unrecognized header fields are treated as
   entity-header fields.

*/

    char c;
    char *value;
    char *line;
    int check_CRLF;

    /*
     * This 'simple' function should only get called if we've read the end
     * of the headers.
     */
    assert(req->p_header_end != NULL);

    req->header_line = 1 + req->p_pos;
    for (;;) {
        line = req->header_line;

        /*
         * At this point, one \r\n (or \n) was just parsed so another immediate
         * \r\n (or \n) means we got the end of the header.
         */
        if (('\r' == line[0]) && ('\n' == line[1])) {
            req->p_entity_body = &line[2];
            req->header_line = &line[2];
            break;
        } else if ('\n' == line[0]) {
            req->p_entity_body = &line[1];
            req->header_line = &line[1];
            break;
        }

        /*
         * Look for the ":" between the header name and value.  If we don't
         * find it, we'll just ignore this header and continue with the next
         * one.
         */
        assert(req->p_header_end != NULL);
        assert(line < req->p_header_end);
        assert(*(req->p_header_end-1) == '\n');
        value = strchr(line, ':');
        if (NULL == value) {
            /* No name/value separator found.  Jump to end of this line */
            req->header_line = strchr(line, '\n');

            /*
             * Since this is a simple_* function, it's only being called if we
             * already read the end of the headers.  It should be impossible
             * to hit the end of the buffer before finding the header end.
             */
            assert(req->header_line != NULL);

            /* Continue parsing with the next header */
            req->header_line++;
            continue;
        }

        /*
         * Figure out which header this is; it looks like this code may have
         * been taken from Boa.
         */
        *value++ = '\0';
        to_upper(line);         // header type is case insensitive
        // while((c=*value)&&(c==' '||c=='\t')) // original
        for (;;) {
            c = *value;

            assert(c != '\0');      /* can't be null in simple_*() */
            if (c == ' ' || c == '\t') {
                /* Ignore whitespace */
                value++;
            } else {
                break;
            }
        }

        if (!memcmp(line, "HOST", 5)) {
            // TODO: Implement virtual hosts

        } else if (!memcmp(line, "IF_MODIFIED_SINCE", 18)
                 && !req->if_modified_since) {
            req->if_modified_since = value;

        } else if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type) {
            req->content_type = value;

        } else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length) {
            req->content_length = value;

        } else if (!memcmp(line, "CONNECTION", 11)
                 // && ka_max 
                 // && req->keepalive != KA_STOPPED)
                 && ka_max_con && req->ka_status != KA_STOPPED) {
            // TODO: Fix keepalive

        } else if (!memcmp(line, "REFERER", 8)) {
            req->header_referer = value;

        } else if (!memcmp (line, "ACCEPT", 7)) {
            // TODO: Honor Accept: headers

        } else if (!memcmp(line, "USER_AGENT", 11)) {
            req->header_user_agent = value;

        /****** What is this doing here?
        } else if (req->is_cgi) {
            // init value of is_cgi is 0.
            // not yet implemented
            check_CRLF = 0;
         ******/

        }

        /*
         * Jump to end of this header and set parsing point to the
         * beginning of the next header.  Since this is a simple_*
         * function, it's only being called if we already read the end
         * of the headers.  It should be impossible to hit the end of
         * the buffer before finding the header end.
         */
         req->header_line = strchr(value, '\n');
         assert(req->header_line != NULL);
         req->header_line++;
    }
}
 
 


Legend
Lines deleted from 1.15  
Lines Modified
  Lines added in revision 1.11