Revision 1.7 |
Revision 1.15 |
Line 6 |
Line 6 |
#include "defines.h" #include "response.h"  
|
#include "defines.h" #include "response.h"  
|
void simple_parse_header(msg*);
|
#define MIN(a,b) (a <= b ? a : b)
|
int simple_parse_request(msg*);
int simple_parse_request_line(msg*);
|
|
/**** * read_request()
|
/**** * read_request()
|
Line 21 |
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 */
|
/* Initialize connection settings */
|
req->simple = 1; /* Assume HTTP/0.9 by default */
|
|
req->ka_status = KA_ACTIVE; /* 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_status = KA_ACTIVE; /* 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->status = START_LINE;
req->is_cgi = 0;
/* RFC 2616 - Section 5
Request = Request-Line ; Section 5.1
*(( general-header ; Section 4.5
| request-header ; Section 5.3
| entity-header ) CRLF) ; Section 7.1
CRLF
[ message-body ] ; Section 4.3
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
*/
/* man 2 recv
All three routines return the length of the message on
successful completion. If a message is too long to fit in
the supplied buffer, excess bytes may be discarded depend-
ing on the type of socket the message is received from
(see socket(2)).
If no messages are available at the socket, the receive
calls wait for a message to arrive, unless the socket is
nonblocking (see fcntl(2)) in which case the value -1 is
returned and the external variable errno set to EAGAIN.
The receive calls normally return any data available, up
to the requested amount, rather than waiting for receipt
of the full amount requested.
*/
/* additional initialization */
|
/* 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);
}
//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;
}
//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;
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 */
pth_event_t ev_read;
/* Clear our buffers and prepare for the next request (if any) */
|
req->p_buf_left = MAX_BUF_SIZE - 1; // off 1 to put '\0' req->p_buf_head = req->p_buf;
|
req->p_buf_left = MAX_BUF_SIZE - 1; // off 1 to put '\0' req->p_buf_head = req->p_buf;
|
|
req->p_header_end = NULL;
|
req->p_eof = 0; req->p_buf_read = 0; req->p_entity_body = NULL;
|
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_more(req);
for (;;) // keep-alive loop
{
/* If a socket error occurs, just close the connection */
if (ret_val < 0) {
COW_CLOSE(req->fdp);
|
/*
* Set the timeout for reading to be the minimum of either the
* keepalive timeout or the regular socket timeout.
*/
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 */
|
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. appropreate response has been sent.
COW_CLOSE(req->fdp);
return -1;
}
} 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.
*/
ret_val = parse_request(req);
if (0 != ret_val) {
COW_CLOSE(req->fdp);
return -1;
}
}
if ((KA_ACTIVE != req->ka_status) || (1 > --req->ka_count)) {
COW_CLOSE(req->fdp) return ret_val;
}
/* 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()
inline int read_request_ka(msg * req) {
/* timeout */
pth_event_t ev_read;
// extend this to handle ka.
ev_read = pth_event(PTH_EVENT_TIME, pth_timeout(ka_max_dur, 0));
// read and recv are equivalent, so pick one
// if ka timeout -> -1
|
|
req->p_buf_read = pth_read_ev(req->fdp, req->p_buf_head, req->p_buf_left, ev_read);
|
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);
|
pth_event_free(ev_read, PTH_FREE_THIS);
|
// 0 is returned iff EOF was read, and it's the only char being read.
//if( 0 == req->p_buf_read) { return 0; }
|
|
if (0 == req->p_buf_read) {
|
if (0 == req->p_buf_read) {
|
|
/* 0 is returned iff EOF was read, and it was the only char read */
|
req->p_eof = 1;
|
req->p_eof = 1;
|
return 1;
}
if (0 > req->p_buf_read) {
// read error, possible bad connection.
return (-1);
|
return -1;
} else if (0 > req->p_buf_read) {
/* read error, timeout or bad connection */
return -1;
}
|
}
req->p_buf_left -= req->p_buf_read;
req->p_buf_head = (char *)(&req->p_buf_head[req->p_buf_read]);
{
char *t = req->p_buf_head;
if (10 == (int)*(t - 1) &&
13 == (int)*(t - 2) && 10 == (int)*(t - 3) && 13 == (int)*(t - 4)) {
req->p_header_end = t;
return 1;
}
}
req->p_buf_head[0] = '\0';
req->p_header_end = NULL;
return 0;
}
/****
* read_request_more()
*
* 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_more(msg * req) {
/* timeout */
pth_event_t ev_read;
/*
* 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_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]);
|
Line 220 |
Line 193 |
} }
|
} }
|
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
|
/****
* 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.
*
* @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) {
|
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:
PRINT("def...\n");
response_default(req->fdp);
return -1;
}
return 0;
}
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 286 |
Line 230 |
uri = logline + 4; } else { // bad request or not implemented
|
uri = logline + 4; } else { // bad request or not implemented
|
response_501(req->fdp);
|
//printf("DEBUG: Service = '%s'\n", logline);
|
|
STATIC_RESPONSE(req->fdp, 501);
|
return -1; } /* Scan to start of non-whitespace (i.e. the URI) */
|
return -1; } /* Scan to start of non-whitespace (i.e. the URI) */
|
while (*(++uri) == ' ') {
|
while (*(++uri) == ' ')
|
|
;
/* scan to end of URI */
stop = uri;
for (;;) {
|
/*
|
/*
|
* Make sure we don't scan off the end of the input in our attempt to
* find non-whitespace.
|
* 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 (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
|
if (*stop == '\0' || *stop == ' ')
break;
/*
* \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') {
STATIC_RESPONSE(req->fdp, 400);
return -1;
}
/* Not there yet... */
|
* pointer back one character and try to read some more.
*/
--uri;
if (0 > read_request_more(req))
return -1;
}
}
stop = uri;
/* BNF: SP */
/* scan to end of non-whitespace */
while (*stop != '\0' && *stop != ' ') {
|
|
++stop;
|
++stop;
|
if (stop > req->p_buf_head) {
/* We just scanned past the end of the current input. */
|
}
/* If the URI is too long, return an error page */
|
if (0 >= req->p_buf_left) {
/* Request too long to parse */
return -1;
}
/*
* Back up one character and try to read some more from the
* network
*/
--stop;
if (0 > read_request_more(req))
return -1;
}
}
|
|
if (stop - uri > MAX_REQ_URI_LEN) {
|
if (stop - uri > MAX_REQ_URI_LEN) {
|
response_414(req->fdp);
|
STATIC_RESPONSE(req->fdp, 414);
|
return -1; }
|
return -1; }
|
|
/* Record the URI in the request structure */
|
req->p_uri_len = stop - uri; memcpy(req->p_uri, uri, req->p_uri_len); req->p_uri[req->p_uri_len] = '\0';
|
req->p_uri_len = stop - uri; memcpy(req->p_uri, uri, req->p_uri_len); req->p_uri[req->p_uri_len] = '\0';
|
Line 360 |
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 (0 == p1 || 0 == p2)
|
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", req->p_uri, stop); #endif
|
} else { #ifdef LOGGING fprintf(ELOG, "Malformed request '%s %s'\n", req->p_uri, stop); #endif
|
response_400(req->fdp);
|
STATIC_RESPONSE(req->fdp, 400);
|
return -1; } } else {
|
return -1; } } else {
|
Line 376 |
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.
*/
while (*(++stop) != '\0') {
if (stop > req->p_buf_head) {
/* Scanned past end of input */
if (0 == req->p_buf_left)
/* request too long; exceeded server capacity */
return -1;
/*
* Back up one character and try to read some more of the
* request from the network.
*/
--stop;
if (0 > read_request_more(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 ('\n' == *stop) {
req->status = ONE_CRLF;
return 0;
} else if ('\r' == *stop && '\n' == *(1 + stop)) {
req->status = ONE_CRLF;
req->p_pos = stop + 1;
return 0;
}
}
// unreachable
assert(0);
return -1; /* error */
|
|
} /*
|
} /*
|
Line 498 |
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 523 |
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_more(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_more(req);
if (retval < 0)
return -1;
continue;
|
|
} else if (c == ' ' || c == '\t') { /* Ignore whitespace */ value++;
|
} else if (c == ' ' || c == '\t') { /* Ignore whitespace */ value++;
|
Line 571 |
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)
&& ka_max_con && req->ka_status != KA_STOPPED) {
// TODO: Fix keepalive
|
// TODO
} else if (!memcmp(line, "CONNECTION", 11) &&
ka_max_con && req->ka_status != KA_STOPPED) {
if (0 == memcmp(value, "close", 5)) {
req->ka_status = KA_INACTIVE;
}
|
|
// 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_more(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:
PRINT("def...\n");
response_default(req->fdp);
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
response_501(req->fdp);
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;
/* 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) {
response_414(req->fdp);
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
response_400(req->fdp);
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 ('\n' == *stop) {
req->status = ONE_CRLF;
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;
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.
*/
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++;
}
}
|
|