Browse Source

[core] http_request_parse_target()

http_request_parse_target() split from http_response_prepare()
personal/stbuehler/ci-build
Glenn Strauss 2 years ago
parent
commit
d013d0abd3
  1. 2
      src/CMakeLists.txt
  2. 2
      src/Makefile.am
  3. 2
      src/meson.build
  4. 136
      src/request.c
  5. 1
      src/request.h
  6. 123
      src/response.c

2
src/CMakeLists.txt

@ -841,7 +841,9 @@ add_test(NAME test_mod_userdir COMMAND test_mod_userdir)
add_executable(test_request
t/test_request.c
request.c
base64.c
buffer.c
burl.c
array.c
data_integer.c
data_string.c

2
src/Makefile.am

@ -587,7 +587,7 @@ t_test_mod_simple_vhost_LDADD = $(LIBUNWIND_LIBS)
t_test_mod_userdir_SOURCES = t/test_mod_userdir.c buffer.c array.c data_integer.c data_string.c log.c
t_test_mod_userdir_LDADD = $(LIBUNWIND_LIBS)
t_test_request_SOURCES = t/test_request.c request.c buffer.c array.c data_integer.c data_string.c http_header.c http_kv.c log.c sock_addr.c
t_test_request_SOURCES = t/test_request.c request.c base64.c buffer.c burl.c array.c data_integer.c data_string.c http_header.c http_kv.c log.c sock_addr.c
t_test_request_LDADD = $(LIBUNWIND_LIBS)
noinst_HEADERS = $(hdr)

2
src/meson.build

@ -839,7 +839,9 @@ test('test_request', executable('test_request',
sources: [
't/test_request.c',
'request.c',
'base64.c',
'buffer.c',
'burl.c',
'array.c',
'data_integer.c',
'data_string.c',

136
src/request.c

@ -516,7 +516,7 @@ static int http_request_parse_proto_loose(request_st * const restrict r, const c
}
__attribute_cold__
static const char * http_request_parse_uri_alt(request_st * const restrict r, const char * const restrict uri, const size_t len, const unsigned int http_parseopts) {
static const char * http_request_parse_reqline_uri(request_st * const restrict r, const char * const restrict uri, const size_t len, const unsigned int http_parseopts) {
const char *nuri;
if ((len > 7 && buffer_eq_icase_ssn(uri, "http://", 7)
&& NULL != (nuri = memchr(uri + 7, '/', len-7)))
@ -611,7 +611,7 @@ static int http_request_parse_reqline(request_st * const restrict r, const char
len = (size_t)(p - uri - 1);
if (*uri != '/') { /* (common case: (*uri == '/')) */
uri = http_request_parse_uri_alt(r, uri, len, http_parseopts);
uri = http_request_parse_reqline_uri(r, uri, len, http_parseopts);
if (NULL == uri) return 400;
len = (size_t)(p - uri - 1);
}
@ -642,6 +642,134 @@ static int http_request_parse_reqline(request_st * const restrict r, const char
return 0;
}
int http_request_parse_target(request_st * const r, int scheme_port) {
/* URI is parsed into components at start of request and may
* also be re-parsed upon HANDLER_COMEBACK during the request
* r->target is expected to be a "/url-part?query-part"
* (and *not* a fully-qualified URI starting https://...)
* r->uri.authority is expected to be parsed elsewhere into r->http_host
*/
/**
* prepare strings
*
* - uri.path_raw
* - uri.path
* - uri.query
*
*/
/**
* Name according to RFC 2396
*
* - scheme
* - authority
* - path
* - query
*
* (scheme)://(authority)(path)?(query)#fragment
*
*/
/* take initial scheme value from connection-level state
* (request r->uri.scheme can be overwritten for later,
* for example by mod_extforward or mod_magnet) */
if (scheme_port == 443)
buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https"));
else
buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("http"));
if (r->http_host) { /*(might not know until after parsing request headers)*/
buffer_copy_buffer(&r->uri.authority, r->http_host);
buffer_to_lower(&r->uri.authority);
}
else {
buffer_string_set_length(&r->uri.authority, 0);
}
buffer * const target = &r->target;
if (r->http_method == HTTP_METHOD_CONNECT
|| (r->http_method == HTTP_METHOD_OPTIONS
&& target->ptr[0] == '*'
&& target->ptr[1] == '\0')) {
/* CONNECT ... (or) OPTIONS * ... */
buffer_copy_buffer(&r->uri.path_raw, target);
buffer_copy_buffer(&r->uri.path, target);
buffer_clear(&r->uri.query);
return 0;
}
char *qstr;
if (r->conf.http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE) {
/*uint32_t len = (uint32_t)buffer_string_length(target);*/
int qs = burl_normalize(target, r->tmp_buf, r->conf.http_parseopts);
if (-2 == qs) {
log_error(r->conf.errh, __FILE__, __LINE__,
"invalid character in URI -> 400 %s",
target->ptr);
return 400; /* Bad Request */
}
qstr = (-1 == qs) ? NULL : target->ptr+qs;
#if 0 /* future: might enable here, or below for all requests */
/* (Note: total header size not recalculated on HANDLER_COMEBACK
* even if other request headers changed during processing)
* (If (0 != r->loops_per_request), then the generated
* request is too large. Should a different error be returned?) */
r->rqst_header_len -= len;
len = buffer_string_length(target);
r->rqst_header_len += len;
if (len > MAX_HTTP_REQUEST_URI) {
return 414; /* 414 Request-URI Too Long */
}
if (r->rqst_header_len > MAX_HTTP_REQUEST_HEADER) {
log_error(r->conf.errh, __FILE__, __LINE__,
"request header fields too large: %u -> 431",
r->rqst_header_len);
return 431; /* Request Header Fields Too Large */
}
#endif
}
else {
size_t rlen = buffer_string_length(target);
qstr = memchr(target->ptr, '#', rlen);/* discard fragment */
if (qstr) {
rlen = (size_t)(qstr - target->ptr);
buffer_string_set_length(target, rlen);
}
qstr = memchr(target->ptr, '?', rlen);
}
/** extract query string from target */
if (NULL != qstr) {
const char * const pstr = target->ptr;
const size_t plen = (size_t)(qstr - pstr);
const size_t rlen = buffer_string_length(target);
buffer_copy_string_len(&r->uri.query, qstr + 1, rlen - plen - 1);
buffer_copy_string_len(&r->uri.path_raw, pstr, plen);
}
else {
buffer_clear(&r->uri.query);
buffer_copy_buffer(&r->uri.path_raw, target);
}
/* decode url to path
*
* - decode url-encodings (e.g. %20 -> ' ')
* - remove path-modifiers (e.g. /../)
*/
buffer_copy_buffer(&r->uri.path, &r->uri.path_raw);
buffer_urldecode_path(&r->uri.path);
buffer_path_simplify(&r->uri.path, &r->uri.path);
if (r->uri.path.ptr[0] != '/') {
log_error(r->conf.errh, __FILE__, __LINE__,
"uri-path does not begin with '/': %s -> 400", r->uri.path.ptr);
return 400; /* Bad Request */
}
return 0;
}
__attribute_cold__
__attribute_noinline__
static int http_request_parse_header_other(request_st * const restrict r, const char * const restrict k, const int klen, const unsigned int http_header_strict) {
@ -810,6 +938,10 @@ int http_request_parse(request_st * const restrict r, char * const restrict hdrs
/* check hostname field if it is set */
if (r->http_host) {
if (buffer_string_is_empty(&r->uri.authority)) {
buffer_copy_buffer(&r->uri.authority, r->http_host);
buffer_to_lower(&r->uri.authority);
}
if (0 != http_request_host_policy(r->http_host,
http_parseopts, scheme_port))
return http_request_header_line_invalid(r, 400, "Invalid Hostname -> 400");

1
src/request.h

@ -178,6 +178,7 @@ struct request_st {
int http_request_parse(request_st * restrict r, char * restrict hdrs, const unsigned short * restrict hloffsets, int scheme_port);
int http_request_parse_target(request_st *r, int scheme_port);
int http_request_host_normalize(buffer *b, int scheme_port);
int http_request_host_policy(buffer *b, unsigned int http_parseopts, int scheme_port);

123
src/response.c

@ -1,6 +1,7 @@
#include "first.h"
#include "response.h"
#include "request.h"
#include "base.h"
#include "burl.h"
#include "fdevent.h"
@ -297,13 +298,10 @@ static handler_t http_status_set_error_close (request_st * const r, int status)
handler_t http_response_prepare(request_st * const r) {
handler_t rc;
/* looks like someone has already done a decision */
/* looks like someone has already made a decision */
if (r->http_status != 0 && r->http_status != 200) {
/* remove a packets in the queue */
if (r->resp_body_finished == 0) {
if (0 == r->resp_body_finished)
http_response_body_clear(r, 0);
}
return HANDLER_FINISHED;
}
@ -320,7 +318,7 @@ handler_t http_response_prepare(request_st * const r) {
*
* mod_compress might add headers twice too
*
* */
*/
if (!r->async_callback) {
@ -330,117 +328,8 @@ handler_t http_response_prepare(request_st * const r) {
log_error(r->conf.errh, __FILE__, __LINE__, "run condition");
}
/**
* prepare strings
*
* - uri.path_raw
* - uri.path
* - uri.query
*
*/
/**
* Name according to RFC 2396
*
* - scheme
* - authority
* - path
* - query
*
* (scheme)://(authority)(path)?(query)#fragment
*
*
*/
/* take initial scheme value from connection-level state
* (request r->uri.scheme can be overwritten for later,
* for example by mod_extforward or mod_magnet) */
if (r->con->proto_default_port == 443)
buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https"));
else
buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("http"));
buffer_copy_buffer(&r->uri.authority, r->http_host);
buffer_to_lower(&r->uri.authority);
buffer * const target = &r->target;
if (r->http_method == HTTP_METHOD_CONNECT
|| (r->http_method == HTTP_METHOD_OPTIONS
&& target->ptr[0] == '*'
&& target->ptr[1] == '\0')) {
/* CONNECT ... (or) OPTIONS * ... */
buffer_copy_buffer(&r->uri.path_raw, target);
buffer_copy_buffer(&r->uri.path, &r->uri.path_raw);
buffer_reset(&r->uri.query);
} else {
char *qstr;
if (r->conf.http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE) {
/*uint32_t len = (uint32_t)buffer_string_length(target);*/
int qs = burl_normalize(target, r->tmp_buf, r->conf.http_parseopts);
if (-2 == qs) {
log_error(r->conf.errh, __FILE__, __LINE__,
"invalid character in URI -> 400 %s",
target->ptr);
return /* 400 Bad Request */
http_status_set_error_close(r, 400);
}
qstr = (-1 == qs) ? NULL : target->ptr+qs;
#if 0 /* future: might enable here, or below for all requests */
/* (Note: total header size not recalculated on HANDLER_COMEBACK
* even if other request headers changed during processing)
* (If (0 != r->loops_per_request), then the generated
* request is too large. Should a different error be returned?) */
r->rqst_header_len -= len;
len = buffer_string_length(target);
r->rqst_header_len += len;
if (len > MAX_HTTP_REQUEST_URI) {
return /* 414 Request-URI Too Long */
http_status_set_error_close(r, 414);
}
if (r->rqst_header_len > MAX_HTTP_REQUEST_HEADER) {
log_error(r->conf.errh, __FILE__, __LINE__,
"request header fields too large: %u -> 431", r->rqst_header_len);
return /* 431 Request Header Fields Too Large */
http_status_set_error_close(r, 431);
}
#endif
} else {
size_t rlen = buffer_string_length(target);
qstr = memchr(target->ptr, '#', rlen);/* discard fragment */
if (qstr) {
rlen = (size_t)(qstr - target->ptr);
buffer_string_set_length(target, rlen);
}
qstr = memchr(target->ptr, '?', rlen);
}
/** extract query string from target */
if (NULL != qstr) {
const char * const pstr = target->ptr;
const size_t plen = (size_t)(qstr - pstr);
const size_t rlen = buffer_string_length(target);
buffer_copy_string_len(&r->uri.query, qstr + 1, rlen - plen - 1);
buffer_copy_string_len(&r->uri.path_raw, pstr, plen);
} else {
buffer_reset(&r->uri.query);
buffer_copy_buffer(&r->uri.path_raw, target);
}
/* decode url to path
*
* - decode url-encodings (e.g. %20 -> ' ')
* - remove path-modifiers (e.g. /../)
*/
buffer_copy_buffer(&r->uri.path, &r->uri.path_raw);
buffer_urldecode_path(&r->uri.path);
buffer_path_simplify(&r->uri.path, &r->uri.path);
if (buffer_string_is_empty(&r->uri.path) || r->uri.path.ptr[0] != '/') {
log_error(r->conf.errh, __FILE__, __LINE__,
"uri-path does not begin with '/': %s -> 400", r->uri.path.ptr);
return /* 400 Bad Request */
http_status_set_error_close(r, 400);
}
}
int status = http_request_parse_target(r, r->con->proto_default_port);
if (0 != status) return http_status_set_error_close(r, status);
r->conditional_is_valid |= (1 << COMP_SERVER_SOCKET)
| (1 << COMP_HTTP_SCHEME)

Loading…
Cancel
Save