Revision 1.13 |
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) { cow_close_socket(req->fdp); --cow_cur_conn; return (ret_val); }
|
if (ret_val < 0) { cow_close_socket(req->fdp); --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.
int zero = 0;
|
//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);
|
cow_close_socket(req->fdp);
|
--cow_cur_conn;
return -1;
|
return len;
} else if (len == 0) {
|
|
/* Blank header line --> end of headers */
break;
|
}
|
}
|
} else {
int zero = 0;
/*
* 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);
|
//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
|
#ifdef USE_TCPCORK
|
setsockopt(req->fdp, SOL_TCP, TCP_CORK, &zero, sizeof(zero));
#endif
if (0 != ret_val) {
cow_close_socket(req->fdp);
--cow_cur_conn;
return -1;
}
}
|
/* 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 */
|
if ((KA_ACTIVE != req->ka_status) || (1 > --req->ka_count)) {
|
|
cow_close_socket(req->fdp); --cow_cur_conn;
|
cow_close_socket(req->fdp); --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 166 |
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));
#ifdef USE_TCPCORK
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) {
// 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;
}
cow_tot_parsed++;
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");
#ifdef ENABLE_PUT
case M_PUT:
PRINT("method is put....\n");
process_header_end(req);
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);
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 326 |
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 415 |
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 431 |
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 544 |
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 569 |
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++;
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
|
/* * 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 618 |
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);
cow_tot_parsed++;
switch (req->p_method) {
case M_HEAD:
case M_GET:
response_get(req);
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");
#ifdef ENABLE_PUT
case M_PUT:
PRINT("method is put\n");
process_header_end(req);
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);
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++;
}
}
|
|