Revision 1.6 |
Revision 1.15 |
#include <sys/types.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include "pth.h"
|
|
#include "cow.h" #include "config.h" #include "cow_config.h"
|
#include "cow.h" #include "config.h" #include "cow_config.h"
|
Line 16 |
Line 6 |
#include "defines.h" #include "response.h"
|
#include "defines.h" #include "response.h"
|
int simple_parse_header(msg*);
int simple_parse_request(msg*);
int simple_parse_request_line(msg*);
/*
* Name: read_request
* Description: see request.h
*/
int read_request(msg * req) {
|
#define MIN(a,b) (a <= b ? a : b)
/****
* read_request()
*
* Reads an HTTP request from a request socket. This function will return
* when it has read at least to the end of the message headers, although
* it may also read part of the request body (e.g. for POST) as well.
*
* @param req A request structure containing the file descriptor to read from
|
|
* and buffers to read into.
* @return 0 or 1 on success, -1 on failure
****/
int read_request(msg* req) {
|
int ret_val;
|
int ret_val;
|
/* initialize fields in msg* req */
|
/* Initialize connection settings */
|
req->simple = 1;
|
|
req->ka_status = KA_ACTIVE; /* by default by http/1.1 */
|
req->ka_status = KA_ACTIVE; /* by default by http/1.1 */
|
req->ka_count = ka_max_con - 1; /* revise here, and read config && use int */
|
req->ka_count = ka_max_con - 1; /* Limit keep-alive req's remaining */
|
req->ka_timeout = ka_max_dur + time(NULL);
|
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
|
/* 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;
|
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 */
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;
//cleaning buf is not needed.
//memset( req->p_buf, 0, sizeof(req->p_buf));
//bzero(req->p_buf, sizeof(req->p_buf));
req->p_entity_body = NULL;
/* read the [first] portion for the first time */
ret_val = read_request_more(req);
for (;;) // keep-alive loop
{
if (0 > ret_val) {
PRINT2("ret_val=%d < 0 @request.c <#openfile %d>\n", ret_val,
cow_noof);
COW_CLOSE(req->fdp);
|
|
return (ret_val); }
|
return (ret_val); }
|
if (1 == ret_val) // read \r\n\r\n
{
// 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;
|
//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 // i.e. ( 0 == ret_val)
{
// The end of HTTP message was not \r\n\r\n , which means one of
// 1. Read header not completed.
// 2. Message contains body (and read part of 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()
|
//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;
|
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
|
/* 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_header_end = NULL;
req->p_eof = 0;
|
|
req->p_buf_read = 0;
req->p_entity_body = NULL;
/*
* 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 */
|
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;
} 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 += 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;
|
return 1;
|
}
if (0 > req->p_buf_read) {
// read error, possible bad connection.
return (-1);
|
} else {
/* Still haven't seen the end of the request */
req->p_header_end = NULL;
return 0;
}
|
}
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;
|
|
}
|
}
|
inline int read_request_more(msg * req) {
/* timeout */
pth_event_t ev_read;
// extend this to handle ka.
ev_read = pth_event(PTH_EVENT_TIME, pth_timeout(cow_rto_s, cow_rto_u));
// read and recv are equivalent, so pick one
// if read timeout -> -1
req->p_buf_read =
//1
//pth_read_ev (req->fdp, req->p_buf_head, req->p_buf_left, ev_read) ;
//2
pth_read(req->fdp, req->p_buf_head, req->p_buf_left);
|
/****
* 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) {
|
pth_yield(NULL);
//3
//read(req->fdp, req->p_buf_head, req->p_buf_left);
//
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) {
req->p_eof = 1;
return 1;
}
if (0 > req->p_buf_read) {
// read error, possible 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]);
req->p_buf_head[0] = '\0';
req->p_header_end = NULL;
{
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;
}
}
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
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 */
|
char *stop, *stop2;
|
char *uri, *stop;
|
char *logline = req->p_buf; /* BNF: Method */
|
char *logline = req->p_buf; /* BNF: Method */
|
if (!memcmp(logline, "GET ", 4))
|
if (!memcmp(logline, "GET ", 4)) {
|
req->p_method = M_GET;
|
req->p_method = M_GET;
|
else if (!memcmp(logline, "PUT ", 4))
|
uri = logline + 3;
|
|
} else if (!memcmp(logline, "PUT ", 4)) {
|
req->p_method = M_PUT;
|
req->p_method = M_PUT;
|
else if (!memcmp(logline, "HEAD ", 5))
|
uri = logline + 3;
|
|
} else if (!memcmp(logline, "HEAD ", 5)) {
|
req->p_method = M_HEAD;
|
req->p_method = M_HEAD;
|
else if (!memcmp(logline, "POST ", 5))
|
uri = logline + 4;
|
|
} else if (!memcmp(logline, "POST ", 5)) {
|
req->p_method = M_POST;
|
req->p_method = M_POST;
|
else {
// bad request OR not implemented
response_default(req->fdp);
COW_CLOSE(req->fdp) return -1;
}
/* BNF: SP */
/* Guaranteed to find ' ' since we matched a method above */
stop = logline + 3;
if (*stop != ' ')
|
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) */
while (*(++uri) == ' ')
|
|
;
/* scan to end of URI */
stop = uri;
for (;;) {
/*
* 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 == ' ')
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... */
|
++stop;
|
++stop;
|
/* scan to start of non-whitespace */
while (*(++stop) == ' ') {
if (stop > req->p_buf_head) {
if (0 >= req->p_buf_left) {
|
}
/* If the URI is too long, return an error page */
if (stop - uri > MAX_REQ_URI_LEN) {
STATIC_RESPONSE(req->fdp, 414);
|
//response_400(req->fdp);
//response_500(req->fdp);
return -1;
}
--stop;
if (0 > read_request_more(req))
return -1;
}
}
stop2 = stop;
/* BNF: SP */
/* scan to end of non-whitespace */
while (*stop2 != '\0' && *stop2 != ' ') {
++stop2;
if (stop2 > req->p_buf_head) {
if (0 >= req->p_buf_left) {
//response_500(req->fdp);
//response_400(req->fdp);
return -1;
}
--stop2;
if (0 > read_request_more(req))
return -1;
}
}
if (stop2 - stop > MAX_REQ_URI_LEN) {
response_414(req->fdp);
|
|
return -1; }
|
return -1; }
|
req->p_uri_begin = stop;
req->p_uri_end = stop2;
memcpy(req->p_uri, stop, stop2 - stop);
|
/* 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[stop2 - stop] = '\0';
|
|
/* HTTP-Version */
|
/* HTTP-Version */
|
if (*stop2 == ' ') {
|
if (*stop == ' ') {
|
/* if found, we should get an HTTP/x.x */ int p1, p2;
|
/* if found, we should get an HTTP/x.x */ int p1, p2;
|
if (sscanf(++stop2, "HTTP/%d.%d", &p1, &p2) == 2 && p1 >= 1) {
|
if (sscanf(++stop, "HTTP/%d.%d", &p1, &p2) == 2 && p1 >= 1) {
|
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 {
// log_error_time();
// fprintf (stderr, "bogus HTTP version: \"%s\"\n", stop2);
// 404 bad request (malformed syntax).
response_400(req->fdp);
|
}
} else {
#ifdef LOGGING
fprintf(ELOG, "Malformed request '%s %s'\n",
req->p_uri, stop);
#endif
|
|
STATIC_RESPONSE(req->fdp, 400);
|
return -1; }
|
return -1; }
|
}
else {
|
} else {
/* No HTTP/x.y present; use HTTP/0.9 */
|
req->simple = 1; req->ka_status = KA_INACTIVE; }
|
req->simple = 1; req->ka_status = KA_INACTIVE; }
|
while (*(++stop2) != '\0') {
|
return 0;
|
if (stop2 > req->p_buf_head) {
if (0 == req->p_buf_left) // exceeded server capacity.
{
//response_500(req->fdp);
return -1;
}
--stop2;
if (0 > read_request_more(req))
return -1;
}
if ('\r' == *(stop2) && '\n' == (*(1 + stop2))) {
req->status = ONE_CRLF;
req->p_pos = stop2 + 1;
// at this point, we know the request-line can be processed
// so, return to the caller
return 0;
}
}
// unreachable
return -1; /* error */
|
|
} /*
|
} /*
|
Line 447 |
Line 379 |
header fields if all parties in the communication recognize them to be request-header fields. Unrecognized header fields are treated as entity-header fields.
|
header fields if all parties in the communication recognize them to be request-header fields. Unrecognized header fields are treated as entity-header fields.
|
|
|
*/
|
*/
|
/*
* Each option line end w/ \r\n.
*/
|
|
char c; char *value; char *line; int check_CRLF;
|
char c; char *value; char *line; int check_CRLF;
|
req->header_line = 1 + req->p_pos;
// process one line at a time.
// return 0 on success
// return -1 on error
// return +1 on incomplete
|
|
for (;;) {
|
for (;;) {
|
/*
* At this point, one \r\n was just parsed.
* So, another immediat \r\n means we got the end of the header.
*/
line = req->header_line;
if (('\r' == line[0]) && ('\n' == line[1])) {
req->p_entity_body = &line[2];
req->header_line = &line[2];
|
line = req->p_buf;
/*
* 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)
|
return 0;
|
return 0;
|
}
value = strchr(line, ':');
if (NULL == value) {
response_400(req->fdp);
|
/*
* Figure out which header this is; it looks like this code may have
* been taken from Boa.
*/
|
return -1; // illegal header line
/****
if ((line + strlen (line)) >= req->p_buf_head)
{
retval = read_request_more (req);
if (0>retval)
return -1;
else
continue; // continue this for loop from the top
}
return -1; // else error in format
****/
}
|
|
*value++ = '\0'; to_upper(line); // header type is case insensitive
|
*value++ = '\0'; to_upper(line); // header type is case insensitive
|
// while((c=*value)&&(c==' '||c=='\t')) // original
|
|
|
/* Find the start of the value */
|
for (;;) {
|
for (;;) {
|
|
int retval;
|
c = *value; if ('\0' == c) {
|
c = *value; if ('\0' == c) {
|
return -1; // bad header
/*
retval = read_request_more (req);
if (retval)
{
return retval;
}
|
/* Empty header value */
return 0;
} else if (c == ' ' || c == '\t') {
/* Ignore whitespace */
value++;
} else {
break;
|
*/
|
|
}
|
}
|
else if (c == ' ' || c == '\t') {
value++;
|
}
|
|
if (!memcmp(line, "HOST", 5)) {
// TODO
} else if (!memcmp(line, "IF_MODIFIED_SINCE", 18)
&& !req->if_modified_since) {
// TODO
} else if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type) {
// TODO
} else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length) {
// 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;
|
}
|
}
|
else
break;
}
check_CRLF = 1;
if (!memcmp(line, "HOST", 5)) {
//not yet
}
else if (!memcmp(line, "IF_MODIFIED_SINCE", 18)
&& !req->if_modified_since) {
req->if_modified_since = value;
}
|
// TODO: deal with other "connection" headers
} else if (!memcmp(line, "REFERER", 8)) {
#ifdef LOGGING
// TODO
#endif
} else if (!memcmp (line, "ACCEPT", 7)) {
// TODO: Honor Accept: headers
} else if (!memcmp(line, "USER_AGENT", 11)) {
#ifdef LOGGING
|
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) {
// not yet
}
else if (!memcmp(line, "REFERER", 8)) {
req->header_referer = value;
}
/*
else if (!memcmp (line, "ACCEPT", 7))
{
//req->header_referer = value;
// need to add_accept_header(req,val); -> not yet.
}
*/
else if (!memcmp(line, "USER_AGENT", 11)) {
|
|
req->header_user_agent = value;
|
req->header_user_agent = value;
|
}
else if (req->is_cgi) {
|
#endif
}
}
|
// init value of is_cgi is 0.
// not yet implemented
check_CRLF = 0;
}
else {
// not implemented yet...
// but silently ignore for now...
//check_CRLF=0;
//check_CRLF=1;
}
/*
* PURPOSE:
* The body of this if-stmt is to parse the current header line so that
* 1. get the CRLF at the end of the line (should be there), and
* 2. make the parsing point (p_pos or header_line) to the beginning of
* the next line. (so that if the immediate next chars are \r\n then
* finish parsing header at the top of this loop).
* Note that some categories above just assign the pointer to the appropreate
* entry of req, so we need to find the end of the line in these cases.
*/
if (check_CRLF) {
int flg_cr = 0;
char *val;
val = value;
val++;
for (;;) {
if ('\0' == *val) {
return -1;
/*
retval = read_request_more (req);
if (retval)
return -1;
else
continue;
*/
}
else if ('\r' == *val)
flg_cr = 1;
else if ('\n' == *val) {
if (flg_cr) {
// alright, we got \r\n sequence.
req->header_line = ++val;
break;
}
else
flg_cr = 0;
}
else if (flg_cr)
flg_cr = 0;
val++;
}
}
}
return 0; // success
|
|
}
|
}
|
/*** 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 header lines
/*
*/
retval = simple_parse_header(req);
if (retval)
return retval;
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 *stop, *stop2;
char *logline = req->p_buf;
/* Method */
if (!memcmp(logline, "GET ", 4))
req->p_method = M_GET;
else if (!memcmp(logline, "PUT ", 4))
req->p_method = M_PUT;
else if (!memcmp(logline, "HEAD ", 5))
req->p_method = M_HEAD;
else if (!memcmp(logline, "POST ", 5))
req->p_method = M_POST;
else {
response_501(req->fdp);
return -1;
}
/* SP */
/* Guaranteed to find ' ' since we matched a method above */
stop = logline + 3;
if (*stop != ' ')
++stop;
/* scan to start of non-whitespace */
while (*(++stop) == ' ') {
if (stop > req->p_buf_head)
return -1;
// i.e. bogus header
}
stop2 = stop;
/* SP */
/* scan to end of non-whitespace */
while (*stop2 != '\0' && *stop2 != ' ') {
++stop2;
if (stop2 > req->p_buf_head)
return -1;
}
if (stop2 - stop > MAX_REQ_URI_LEN) {
response_414(req->fdp);
return -1;
}
req->p_uri_begin = stop;
req->p_uri_end = stop2;
PRINT1("URI: %s\n", req->p_uri_begin);
memcpy(req->p_uri, stop, stop2 - stop);
req->p_uri[stop2 - stop] = '\0';
/* HTTP-Version */
if (*stop2 == ' ') {
/* if found, we should get an HTTP/x.x */
int p1, p2;
if (sscanf(++stop2, "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 {
// log_error_time();
/* should one of:
| "412" ; Section 10.4.13: Precondition Failed
| "417" ; Section 10.4.18: Expectation Failed
| "501" ; Section 10.5.2: Not Implemented
| "503" ; Section 10.5.4: Service Unavailable
| "505" ; Section 10.5.6: HTTP Version not supported
*/
response_501(req->fdp);
return -1;
}
}
else {
req->simple = 1;
req->ka_status = KA_INACTIVE;
}
while (*(++stop2) != '\0') {
if ('\r' == *(stop2) && '\n' == (*(1 + stop2))) {
req->status = ONE_CRLF;
req->p_pos = stop2 + 1;
return 0;
}
}
// should be unreachable because we know \r\n\r\n was found
return -1; /* error */
}
/*
* 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
*/
int 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.
*/
/*
* Each option line end w/ \r\n.
*/
char c;
char *value;
char *line;
int check_CRLF;
req->header_line = 1 + req->p_pos;
// process one line at a time.
// return 0 on success
// return -1 on error
// return +1 on incomplete
for (;;) {
/*
* At this point, one \r\n was just parsed.
* So, another immediat \r\n means we got the end of the header.
*/
line = req->header_line;
if (('\r' == line[0]) && ('\n' == line[1])) {
req->p_entity_body = &line[2];
req->header_line = &line[2];
return 0;
}
value = strchr(line, ':');
if (NULL == value) {
return -1; // illegal header line
}
*value++ = '\0';
to_upper(line); // header type is case insensitive
// while((c=*value)&&(c==' '||c=='\t')) // original
for (;;) {
c = *value;
if ('\0' == c) {
return -1; // bad header
}
else if (c == ' ' || c == '\t') {
value++;
}
else
break;
}
check_CRLF = 1;
if (!memcmp(line, "HOST", 5)) {
//not yet
}
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) {
// not yet
}
else if (!memcmp(line, "REFERER", 8)) {
req->header_referer = value;
}
/*
else if (!memcmp (line, "ACCEPT", 7))
{
//req->header_referer = value;
// need to add_accept_header(req,val); -> not yet.
}
*/
else if (!memcmp(line, "USER_AGENT", 11)) {
req->header_user_agent = value;
}
else if (req->is_cgi) {
// init value of is_cgi is 0.
// not yet implemented
check_CRLF = 0;
}
else {
// not implemented yet...
// but silently ignore for now...
//check_CRLF=0;
//check_CRLF=1; // this is default
}
/*
* PURPOSE:
* The body of this if-stmt is to parse the current header line so that
* 1. get the CRLF at the end of the line (should be there), and
* 2. make the parsing point (p_pos or header_line) to the beginning of
* the next line. (so that if the immediate next chars are \r\n then
* finish parsing header at the top of this loop).
* Note that some categories above just assign the pointer to the appropreate
* entry of req, so we need to find the end of the line in these cases.
*/
if (check_CRLF) {
int flg_cr = 0;
char *val;
val = value;
val++;
for (;;) {
if ('\0' == *val) {
return -1;
}
else if ('\r' == *val)
flg_cr = 1;
else if ('\n' == *val) {
if (flg_cr) {
// alright, we got \r\n sequence.
req->header_line = ++val;
break;
}
else
flg_cr = 0;
}
else if (flg_cr)
flg_cr = 0;
val++;
} //end of for-loop
} //end of if-stmt
} // end of for-loop
return 0; // success, but unreachable
}
|
|