Compare commits
3 Commits
master
...
http-parse
Author | SHA1 | Date |
---|---|---|
Tatsuhiro Tsujikawa | 7a84d51aaf | |
Tatsuhiro Tsujikawa | f90f40e3b5 | |
Tatsuhiro Tsujikawa | f38babe30f |
|
@ -53,23 +53,20 @@ func TestH1H1PlainGETClose(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestH1H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501 status code
|
||||
func TestH1H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
// TestH1H1UnknownMethod tests that server can forward unknown method
|
||||
func TestH1H1UnknownMethod(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1InvalidMethod",
|
||||
name: "TestH1H1UnknownMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 501; got != want {
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -589,22 +589,19 @@ func TestH2H1InvalidRequestCL(t *testing.T) {
|
|||
// }
|
||||
// }
|
||||
|
||||
// TestH2H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501.
|
||||
func TestH2H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
// TestH2H1UnknownMethod tests that server can forward unknown method.
|
||||
func TestH2H1UnknownMethod(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1InvalidMethod",
|
||||
name: "TestH2H1UnknownMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 501; got != want {
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,22 +210,19 @@ func TestS3H1HeaderFields(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestS3H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501.
|
||||
func TestS3H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
// TestS3H1UnknownMethod tests that server can forward unknown method.
|
||||
func TestS3H1UnknownMethod(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1InvalidMethod",
|
||||
name: "TestS3H1UnknownMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 501; got != want {
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -824,7 +824,7 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
|||
// proxy mode falls in this case.
|
||||
if (groups.size() == 1) {
|
||||
group_idx = 0;
|
||||
} else if (req.method == HTTP_CONNECT) {
|
||||
} else if (req.method_token == HTTP_CONNECT) {
|
||||
// We don't know how to treat CONNECT request in host-path
|
||||
// mapping. It most likely appears in proxy scenario. Since we
|
||||
// have dealt with proxy case already, just use catch-all group.
|
||||
|
@ -1040,14 +1040,14 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
|
|||
upstream_accesslog(
|
||||
get_config()->logging.access.format,
|
||||
LogSpec{
|
||||
downstream, StringRef{ipaddr_}, http2::to_method_string(req.method),
|
||||
downstream, StringRef{ipaddr_}, req.method,
|
||||
|
||||
req.method == HTTP_CONNECT
|
||||
req.method_token == HTTP_CONNECT
|
||||
? StringRef(req.authority)
|
||||
: get_config()->http2_proxy
|
||||
? StringRef(construct_absolute_request_uri(balloc, req))
|
||||
: req.path.empty()
|
||||
? req.method == HTTP_OPTIONS
|
||||
? req.method_token == HTTP_OPTIONS
|
||||
? StringRef::from_lit("*")
|
||||
: StringRef::from_lit("-")
|
||||
: StringRef(req.path),
|
||||
|
|
|
@ -635,7 +635,7 @@ bool Downstream::validate_response_recv_body_length() const {
|
|||
}
|
||||
|
||||
void Downstream::check_upgrade_fulfilled() {
|
||||
if (req_.method == HTTP_CONNECT) {
|
||||
if (req_.method_token == HTTP_CONNECT) {
|
||||
upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
|
||||
|
||||
return;
|
||||
|
@ -650,13 +650,13 @@ void Downstream::check_upgrade_fulfilled() {
|
|||
}
|
||||
|
||||
void Downstream::inspect_http2_request() {
|
||||
if (req_.method == HTTP_CONNECT) {
|
||||
if (req_.method_token == HTTP_CONNECT) {
|
||||
req_.upgrade_request = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Downstream::inspect_http1_request() {
|
||||
if (req_.method == HTTP_CONNECT) {
|
||||
if (req_.method_token == HTTP_CONNECT) {
|
||||
req_.upgrade_request = true;
|
||||
} else {
|
||||
auto upgrade = req_.fs.header(http2::HD_UPGRADE);
|
||||
|
@ -741,7 +741,7 @@ bool Downstream::get_expect_final_response() const {
|
|||
|
||||
bool Downstream::expect_response_body() const {
|
||||
return !resp_.headers_only &&
|
||||
http2::expect_response_body(req_.method, resp_.http_status);
|
||||
http2::expect_response_body(req_.method_token, resp_.http_status);
|
||||
}
|
||||
|
||||
bool Downstream::expect_response_trailer() const {
|
||||
|
|
|
@ -126,7 +126,7 @@ struct Request {
|
|||
: fs(balloc, 16),
|
||||
recv_body_length(0),
|
||||
unconsumed_body_length(0),
|
||||
method(-1),
|
||||
method_token(-1),
|
||||
http_major(1),
|
||||
http_minor(1),
|
||||
upgrade_request(false),
|
||||
|
@ -158,7 +158,8 @@ struct Request {
|
|||
int64_t recv_body_length;
|
||||
// The number of bytes not consumed by the application yet.
|
||||
size_t unconsumed_body_length;
|
||||
int method;
|
||||
StringRef method;
|
||||
int method_token;
|
||||
// HTTP major and minor version
|
||||
int http_major, http_minor;
|
||||
// Returns true if the request is HTTP upgrade (HTTP Upgrade or
|
||||
|
|
|
@ -103,7 +103,7 @@ int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
|
|||
auto &req = downstream_->request();
|
||||
|
||||
// HTTP/2 disables HTTP Upgrade.
|
||||
if (req.method != HTTP_CONNECT) {
|
||||
if (req.method_token != HTTP_CONNECT) {
|
||||
req.upgrade_request = false;
|
||||
}
|
||||
|
||||
|
@ -251,7 +251,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
|
||||
auto no_host_rewrite = httpconf.no_host_rewrite ||
|
||||
get_config()->http2_proxy ||
|
||||
req.method == HTTP_CONNECT;
|
||||
req.method_token == HTTP_CONNECT;
|
||||
|
||||
// http2session_ has already in CONNECTED state, so we can get
|
||||
// addr_idx here.
|
||||
|
@ -286,15 +286,14 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
nva.reserve(req.fs.headers().size() + 9 + num_cookies +
|
||||
httpconf.add_request_headers.size());
|
||||
|
||||
nva.push_back(
|
||||
http2::make_nv_ls_nocopy(":method", http2::to_method_string(req.method)));
|
||||
nva.push_back(http2::make_nv_ls_nocopy(":method", req.method));
|
||||
|
||||
if (req.method != HTTP_CONNECT) {
|
||||
if (req.method_token != HTTP_CONNECT) {
|
||||
assert(!req.scheme.empty());
|
||||
|
||||
nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme));
|
||||
|
||||
if (req.method == HTTP_OPTIONS && req.path.empty()) {
|
||||
if (req.method_token == HTTP_OPTIONS && req.path.empty()) {
|
||||
nva.push_back(http2::make_nv_ll(":path", "*"));
|
||||
} else {
|
||||
nva.push_back(http2::make_nv_ls_nocopy(":path", req.path));
|
||||
|
@ -326,7 +325,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
if (fwdconf.params) {
|
||||
auto params = fwdconf.params;
|
||||
|
||||
if (get_config()->http2_proxy || req.method == HTTP_CONNECT) {
|
||||
if (get_config()->http2_proxy || req.method_token == HTTP_CONNECT) {
|
||||
params &= ~FORWARDED_PROTO;
|
||||
}
|
||||
|
||||
|
@ -369,7 +368,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", xff->value));
|
||||
}
|
||||
|
||||
if (!get_config()->http2_proxy && req.method != HTTP_CONNECT) {
|
||||
if (!get_config()->http2_proxy && req.method_token != HTTP_CONNECT) {
|
||||
// We use same protocol with :scheme header field
|
||||
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", req.scheme));
|
||||
}
|
||||
|
@ -423,7 +422,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
|
||||
// Add body as long as transfer-encoding is given even if
|
||||
// req.fs.content_length == 0 to forward trailer fields.
|
||||
if (req.method == HTTP_CONNECT || transfer_encoding ||
|
||||
if (req.method_token == HTTP_CONNECT || transfer_encoding ||
|
||||
req.fs.content_length > 0 || req.http2_expect_body) {
|
||||
// Request-body is expected.
|
||||
nghttp2_data_provider data_prd{{}, http2_data_read_callback};
|
||||
|
|
|
@ -2055,13 +2055,6 @@ int Http2Session::handle_downstream_push_promise_complete(
|
|||
}
|
||||
|
||||
auto method_token = http2::lookup_method_token(method->value);
|
||||
if (method_token == -1) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
SSLOG(INFO, this) << "Unrecognized method: " << method->value;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO Rewrite authority if we enabled rewrite host. But we
|
||||
// really don't know how to rewrite host. Should we use the same
|
||||
|
@ -2069,7 +2062,8 @@ int Http2Session::handle_downstream_push_promise_complete(
|
|||
if (authority) {
|
||||
promised_req.authority = authority->value;
|
||||
}
|
||||
promised_req.method = method_token;
|
||||
promised_req.method = method->value;
|
||||
promised_req.method_token = method_token;
|
||||
// libnghttp2 ensures that we don't have CONNECT method in
|
||||
// PUSH_PROMISE, and guarantees that :scheme exists.
|
||||
if (scheme) {
|
||||
|
|
|
@ -117,7 +117,7 @@ int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
|
|||
rv = nghttp2_session_upgrade2(
|
||||
session_, reinterpret_cast<const uint8_t *>(settings_payload.c_str()),
|
||||
settings_payload.size(),
|
||||
http->get_downstream()->request().method == HTTP_HEAD, nullptr);
|
||||
http->get_downstream()->request().method_token == HTTP_HEAD, nullptr);
|
||||
if (rv != 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: "
|
||||
|
@ -296,12 +296,6 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
|
|||
auto scheme = req.fs.header(http2::HD__SCHEME);
|
||||
|
||||
auto method_token = http2::lookup_method_token(method->value);
|
||||
if (method_token == -1) {
|
||||
if (error_reply(downstream, 501) != 0) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For HTTP/2 proxy, we request :authority.
|
||||
if (method_token != HTTP_CONNECT && get_config()->http2_proxy && !authority) {
|
||||
|
@ -309,7 +303,8 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
req.method = method_token;
|
||||
req.method = method->value;
|
||||
req.method_token = method_token;
|
||||
if (scheme) {
|
||||
req.scheme = scheme->value;
|
||||
}
|
||||
|
@ -604,9 +599,11 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
|
||||
auto token = http2::lookup_token(nv.name, nv.namelen);
|
||||
switch (token) {
|
||||
case http2::HD__METHOD:
|
||||
req.method = http2::lookup_method_token(value);
|
||||
case http2::HD__METHOD: {
|
||||
req.method = value;
|
||||
req.method_token = http2::lookup_method_token(value);
|
||||
break;
|
||||
}
|
||||
case http2::HD__SCHEME:
|
||||
req.scheme = value;
|
||||
break;
|
||||
|
@ -1415,7 +1412,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
!get_config()->http2_proxy && (downstream->get_stream_id() % 2) &&
|
||||
resp.fs.header(http2::HD_LINK) &&
|
||||
(downstream->get_non_final_response() || resp.http_status == 200) &&
|
||||
(req.method == HTTP_GET || req.method == HTTP_POST)) {
|
||||
(req.method_token == HTTP_GET || req.method_token == HTTP_POST)) {
|
||||
|
||||
if (prepare_push_promise(downstream) != 0) {
|
||||
// Continue to send response even if push was failed.
|
||||
|
|
|
@ -321,7 +321,7 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
|
||||
auto &balloc = downstream_->get_block_allocator();
|
||||
|
||||
auto connect_method = req.method == HTTP_CONNECT;
|
||||
auto connect_method = req.method_token == HTTP_CONNECT;
|
||||
|
||||
auto &httpconf = get_config()->http;
|
||||
|
||||
|
@ -340,8 +340,7 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
auto buf = downstream_->get_request_buf();
|
||||
|
||||
// Assume that method and request path do not contain \r\n.
|
||||
auto meth = http2::to_method_string(req.method);
|
||||
buf->append(meth);
|
||||
buf->append(req.method);
|
||||
buf->append(" ");
|
||||
|
||||
if (connect_method) {
|
||||
|
@ -354,7 +353,7 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
buf->append("://");
|
||||
buf->append(authority);
|
||||
buf->append(req.path);
|
||||
} else if (req.method == HTTP_OPTIONS && req.path.empty()) {
|
||||
} else if (req.method_token == HTTP_OPTIONS && req.path.empty()) {
|
||||
// Server-wide OPTIONS
|
||||
buf->append("*");
|
||||
} else {
|
||||
|
@ -708,7 +707,7 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
|
||||
// TODO It seems that the cases other than HEAD are handled by
|
||||
// http-parser. Need test.
|
||||
return !http2::expect_response_body(req.method, resp.http_status);
|
||||
return !http2::expect_response_body(req.method_token, resp.http_status);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
|
|
@ -80,6 +80,33 @@ int htp_msg_begin(http_parser *htp) {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int htp_methodcb(http_parser *htp, const char *data, size_t len) {
|
||||
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
||||
auto downstream = upstream->get_downstream();
|
||||
auto &req = downstream->request();
|
||||
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
|
||||
if (req.fs.buffer_size() + len >
|
||||
get_config()->http.request_header_field_buffer) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too large request size="
|
||||
<< req.fs.buffer_size() + len;
|
||||
}
|
||||
assert(downstream->get_request_state() == Downstream::INITIAL);
|
||||
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
req.fs.add_extra_buffer_size(len);
|
||||
|
||||
req.method = concat_string_ref(balloc, req.method, StringRef{data, len});
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int htp_uricb(http_parser *htp, const char *data, size_t len) {
|
||||
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
||||
|
@ -88,8 +115,11 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
|
|||
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
|
||||
// This could be executed more than once, but no harm here.
|
||||
if (htp->method != HTTP_METHOD_UNKNOWN) {
|
||||
// We happen to have the same value for method token.
|
||||
req.method = htp->method;
|
||||
req.method_token = htp->method;
|
||||
}
|
||||
|
||||
if (req.fs.buffer_size() + len >
|
||||
get_config()->http.request_header_field_buffer) {
|
||||
|
@ -104,7 +134,7 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
|
|||
|
||||
req.fs.add_extra_buffer_size(len);
|
||||
|
||||
if (req.method == HTTP_CONNECT) {
|
||||
if (req.method_token == HTTP_CONNECT) {
|
||||
req.authority =
|
||||
concat_string_ref(balloc, req.authority, StringRef{data, len});
|
||||
} else {
|
||||
|
@ -243,7 +273,7 @@ void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req,
|
|||
StringRef path;
|
||||
if (u.field_set & (1 << UF_PATH)) {
|
||||
path = util::get_uri_field(uri.c_str(), u, UF_PATH);
|
||||
} else if (req.method == HTTP_OPTIONS) {
|
||||
} else if (req.method_token == HTTP_OPTIONS) {
|
||||
// Server-wide OPTIONS takes following form in proxy request:
|
||||
//
|
||||
// OPTIONS http://example.org HTTP/1.1
|
||||
|
@ -297,13 +327,11 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
|
||||
req.connection_close = !http_should_keep_alive(htp);
|
||||
|
||||
auto method = req.method;
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
std::stringstream ss;
|
||||
ss << http2::to_method_string(method) << " "
|
||||
<< (method == HTTP_CONNECT ? req.authority : req.path) << " "
|
||||
<< "HTTP/" << req.http_major << "." << req.http_minor << "\n";
|
||||
ss << req.method << " "
|
||||
<< (req.method_token == HTTP_CONNECT ? req.authority : req.path)
|
||||
<< " HTTP/" << req.http_major << "." << req.http_minor << "\n";
|
||||
|
||||
for (const auto &kv : req.fs.headers()) {
|
||||
ss << TTY_HTTP_HD << kv.name << TTY_RST << ": " << kv.value << "\n";
|
||||
|
@ -340,7 +368,7 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
|
||||
if (method != HTTP_CONNECT) {
|
||||
if (req.method_token != HTTP_CONNECT) {
|
||||
http_parser_url u{};
|
||||
rv = http_parser_parse_url(req.path.c_str(), req.path.size(), 0, &u);
|
||||
if (rv != 0) {
|
||||
|
@ -356,7 +384,8 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
|
||||
req.no_authority = true;
|
||||
|
||||
if (method == HTTP_OPTIONS && req.path == StringRef::from_lit("*")) {
|
||||
if (req.method_token == HTTP_OPTIONS &&
|
||||
req.path == StringRef::from_lit("*")) {
|
||||
req.path = StringRef{};
|
||||
} else {
|
||||
req.path = http2::rewrite_clean_path(balloc, req.path);
|
||||
|
@ -482,7 +511,10 @@ http_parser_settings htp_hooks = {
|
|||
htp_hdr_valcb, // http_data_cb on_header_value;
|
||||
htp_hdrs_completecb, // http_cb on_headers_complete;
|
||||
htp_bodycb, // http_data_cb on_body;
|
||||
htp_msg_completecb // http_cb on_message_complete;
|
||||
htp_msg_completecb, // http_cb on_message_complete;
|
||||
nullptr, // http_cb on_chunk_header;
|
||||
nullptr, // http_cb on_chunk_complete;
|
||||
htp_methodcb, // http_data_cb on_method;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
@ -973,7 +1005,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
}
|
||||
#endif // HAVE_MRUBY
|
||||
|
||||
auto connect_method = req.method == HTTP_CONNECT;
|
||||
auto connect_method = req.method_token == HTTP_CONNECT;
|
||||
|
||||
auto buf = downstream->get_response_buf();
|
||||
|
||||
|
|
|
@ -68,9 +68,8 @@ mrb_value request_get_method(mrb_state *mrb, mrb_value self) {
|
|||
auto data = static_cast<MRubyAssocData *>(mrb->ud);
|
||||
auto downstream = data->downstream;
|
||||
const auto &req = downstream->request();
|
||||
auto method = http2::to_method_string(req.method);
|
||||
|
||||
return mrb_str_new(mrb, method.c_str(), method.size());
|
||||
return mrb_str_new(mrb, req.method.c_str(), req.method.size());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -80,6 +79,8 @@ mrb_value request_set_method(mrb_state *mrb, mrb_value self) {
|
|||
auto downstream = data->downstream;
|
||||
auto &req = downstream->request();
|
||||
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
|
||||
check_phase(mrb, data->phase, PHASE_REQUEST);
|
||||
|
||||
const char *method;
|
||||
|
@ -88,13 +89,13 @@ mrb_value request_set_method(mrb_state *mrb, mrb_value self) {
|
|||
if (n == 0) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "method must not be empty string");
|
||||
}
|
||||
|
||||
auto token =
|
||||
http2::lookup_method_token(reinterpret_cast<const uint8_t *>(method), n);
|
||||
if (token == -1) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "method not supported");
|
||||
}
|
||||
|
||||
req.method = token;
|
||||
req.method =
|
||||
make_string_ref(balloc, StringRef{method, static_cast<size_t>(n)});
|
||||
req.method_token = token;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
|
|
@ -220,12 +220,6 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
|||
}
|
||||
|
||||
auto method_token = http2::lookup_method_token(method->value);
|
||||
if (method_token == -1) {
|
||||
if (upstream->error_reply(downstream, 501) != 0) {
|
||||
ULOG(FATAL, upstream) << "error_reply failed";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto is_connect = method_token == HTTP_CONNECT;
|
||||
if (!path || !host || !http2::non_empty_value(host) ||
|
||||
|
@ -264,7 +258,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
|||
return;
|
||||
}
|
||||
|
||||
req.method = method_token;
|
||||
req.method = method->value;
|
||||
req.method_token = method_token;
|
||||
if (is_connect) {
|
||||
req.authority = path->value;
|
||||
} else {
|
||||
|
|
|
@ -28,6 +28,7 @@ EXTRA_DIST = CMakeLists.txt
|
|||
if ENABLE_THIRD_PARTY
|
||||
|
||||
noinst_LTLIBRARIES = libhttp-parser.la
|
||||
libhttp_parser_la_CPPFLAGS = ${AMCPPFLAGS} -DHTTP_PARSER_METHOD_CB=1
|
||||
libhttp_parser_la_SOURCES = \
|
||||
http-parser/http_parser.c \
|
||||
http-parser/http_parser.h
|
||||
|
|
|
@ -280,6 +280,7 @@ enum state
|
|||
{ s_dead = 1 /* important that this is > 0 */
|
||||
|
||||
, s_start_req_or_res
|
||||
, s_res_or_resp_mark_H
|
||||
, s_res_or_resp_H
|
||||
, s_start_res
|
||||
, s_res_H
|
||||
|
@ -298,7 +299,11 @@ enum state
|
|||
|
||||
, s_start_req
|
||||
|
||||
, s_req_method_start
|
||||
, s_req_method
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
, s_req_method_unknown
|
||||
#endif
|
||||
, s_req_spaces_before_url
|
||||
, s_req_schema
|
||||
, s_req_schema_slash
|
||||
|
@ -644,6 +649,9 @@ size_t http_parser_execute (http_parser *parser,
|
|||
const char *url_mark = 0;
|
||||
const char *body_mark = 0;
|
||||
const char *status_mark = 0;
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
const char *method_mark = 0;
|
||||
#endif
|
||||
enum state p_state = (enum state) parser->state;
|
||||
const unsigned int lenient = parser->lenient_http_headers;
|
||||
|
||||
|
@ -692,6 +700,14 @@ size_t http_parser_execute (http_parser *parser,
|
|||
case s_req_fragment:
|
||||
url_mark = data;
|
||||
break;
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
case s_res_or_resp_H:
|
||||
case s_req_method_start:
|
||||
case s_req_method:
|
||||
case s_req_method_unknown:
|
||||
method_mark = data;
|
||||
break;
|
||||
#endif
|
||||
case s_res_status:
|
||||
status_mark = data;
|
||||
break;
|
||||
|
@ -726,9 +742,10 @@ reexecute:
|
|||
parser->content_length = ULLONG_MAX;
|
||||
|
||||
if (ch == 'H') {
|
||||
UPDATE_STATE(s_res_or_resp_H);
|
||||
UPDATE_STATE(s_res_or_resp_mark_H);
|
||||
|
||||
CALLBACK_NOTIFY(message_begin);
|
||||
CALLBACK_NOTIFY_NOADVANCE(message_begin);
|
||||
REEXECUTE();
|
||||
} else {
|
||||
parser->type = HTTP_REQUEST;
|
||||
UPDATE_STATE(s_start_req);
|
||||
|
@ -738,19 +755,46 @@ reexecute:
|
|||
break;
|
||||
}
|
||||
|
||||
case s_res_or_resp_mark_H:
|
||||
assert(ch == 'H');
|
||||
UPDATE_STATE(s_res_or_resp_H);
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
/* TODO This will call on_method even if type == HTTP_BOTH is
|
||||
* used and it later turns out that this is response.
|
||||
* Automatic handling bites us hard.
|
||||
*/
|
||||
MARK(method);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case s_res_or_resp_H:
|
||||
if (ch == 'T') {
|
||||
parser->type = HTTP_RESPONSE;
|
||||
UPDATE_STATE(s_res_HT);
|
||||
} else {
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
if (UNLIKELY(!TOKEN(ch))) {
|
||||
SET_ERRNO(HPE_INVALID_CONSTANT);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (UNLIKELY(ch != 'E')) {
|
||||
parser->method = HTTP_METHOD_UNKNOWN;
|
||||
} else {
|
||||
parser->method = HTTP_HEAD;
|
||||
parser->index = 2;
|
||||
}
|
||||
#else
|
||||
if (UNLIKELY(ch != 'E')) {
|
||||
SET_ERRNO(HPE_INVALID_CONSTANT);
|
||||
goto error;
|
||||
}
|
||||
|
||||
parser->type = HTTP_REQUEST;
|
||||
parser->method = HTTP_HEAD;
|
||||
parser->index = 2;
|
||||
#endif
|
||||
|
||||
parser->type = HTTP_REQUEST;
|
||||
UPDATE_STATE(s_req_method);
|
||||
}
|
||||
break;
|
||||
|
@ -952,7 +996,6 @@ reexecute:
|
|||
break;
|
||||
|
||||
case s_start_req:
|
||||
{
|
||||
if (ch == CR || ch == LF)
|
||||
break;
|
||||
parser->flags = 0;
|
||||
|
@ -963,6 +1006,25 @@ reexecute:
|
|||
goto error;
|
||||
}
|
||||
|
||||
UPDATE_STATE(s_req_method_start);
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
MARK(method);
|
||||
#endif
|
||||
|
||||
CALLBACK_NOTIFY_NOADVANCE(message_begin);
|
||||
REEXECUTE();
|
||||
|
||||
break;
|
||||
|
||||
case s_req_method_start:
|
||||
{
|
||||
enum state next_state = s_req_method;
|
||||
|
||||
if (UNLIKELY(!IS_ALPHA(ch))) {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
|
||||
parser->method = (enum http_method) 0;
|
||||
parser->index = 1;
|
||||
switch (ch) {
|
||||
|
@ -984,12 +1046,26 @@ reexecute:
|
|||
case 'T': parser->method = HTTP_TRACE; break;
|
||||
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
|
||||
default:
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
if (UNLIKELY(!TOKEN(ch))) {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
UPDATE_STATE(s_req_method);
|
||||
|
||||
CALLBACK_NOTIFY(message_begin);
|
||||
parser->method = (unsigned int) HTTP_METHOD_UNKNOWN;
|
||||
|
||||
next_state = s_req_method_unknown;
|
||||
#else
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
#endif
|
||||
}
|
||||
|
||||
UPDATE_STATE(next_state);
|
||||
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
MARK(method);
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -1003,8 +1079,21 @@ reexecute:
|
|||
}
|
||||
|
||||
matcher = method_strings[parser->method];
|
||||
if (ch == ' ' && matcher[parser->index] == '\0') {
|
||||
|
||||
if (ch == ' ') {
|
||||
if (matcher[parser->index] != '\0') {
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
parser->method = (unsigned int) HTTP_METHOD_UNKNOWN;
|
||||
#else
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
#endif
|
||||
}
|
||||
|
||||
UPDATE_STATE(s_req_spaces_before_url);
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
CALLBACK_DATA(method);
|
||||
#endif
|
||||
} else if (ch == matcher[parser->index]) {
|
||||
; /* nada */
|
||||
} else if (IS_ALPHA(ch)) {
|
||||
|
@ -1034,14 +1123,56 @@ reexecute:
|
|||
#undef XX
|
||||
|
||||
default:
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
if (UNLIKELY(!TOKEN(ch))) {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
|
||||
parser->method = (unsigned int) HTTP_METHOD_UNKNOWN;
|
||||
|
||||
UPDATE_STATE(s_req_method_unknown);
|
||||
#else
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
#endif
|
||||
}
|
||||
} else if (ch == '-' &&
|
||||
parser->index == 1 &&
|
||||
parser->method == HTTP_MKCOL) {
|
||||
parser->method = HTTP_MSEARCH;
|
||||
} else {
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
if (UNLIKELY(!TOKEN(ch))) {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
|
||||
parser->method = (unsigned int) HTTP_METHOD_UNKNOWN;
|
||||
|
||||
UPDATE_STATE(s_req_method_unknown);
|
||||
#else
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
#endif
|
||||
}
|
||||
|
||||
++parser->index;
|
||||
break;
|
||||
}
|
||||
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
case s_req_method_unknown:
|
||||
{
|
||||
if (UNLIKELY(ch == '\0')) {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ch == ' ') {
|
||||
UPDATE_STATE(s_req_spaces_before_url);
|
||||
CALLBACK_DATA(method);
|
||||
} else if (UNLIKELY(!TOKEN(ch))) {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
|
@ -1049,6 +1180,7 @@ reexecute:
|
|||
++parser->index;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case s_req_spaces_before_url:
|
||||
{
|
||||
|
@ -2078,6 +2210,9 @@ reexecute:
|
|||
CALLBACK_DATA_NOADVANCE(url);
|
||||
CALLBACK_DATA_NOADVANCE(body);
|
||||
CALLBACK_DATA_NOADVANCE(status);
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
CALLBACK_DATA_NOADVANCE(method);
|
||||
#endif
|
||||
|
||||
RETURN(len);
|
||||
|
||||
|
|
|
@ -53,6 +53,17 @@ typedef unsigned __int64 uint64_t;
|
|||
# define HTTP_PARSER_STRICT 1
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_METHOD_CB=1 to enable method
|
||||
* callback. If it is enabled, method string is notified with
|
||||
* on_method callback. The unknown method which would be rejeted
|
||||
* previously is also accepted and notified with the on_method
|
||||
* callback. The method field of http_parser struct becomes
|
||||
* HTTP_METHOD_UNKNOWN if method is unknown to http_parser.
|
||||
*/
|
||||
#ifndef HTTP_PARSER_METHOD_CB
|
||||
# define HTTP_PARSER_METHOD_CB 0
|
||||
#endif
|
||||
|
||||
/* Maximium header size allowed. If the macro is not defined
|
||||
* before including this header then the default is used. To
|
||||
* change the maximum header size, define the macro in the build
|
||||
|
@ -140,6 +151,8 @@ enum http_method
|
|||
#undef XX
|
||||
};
|
||||
|
||||
/* Unknown HTTP method */
|
||||
#define HTTP_METHOD_UNKNOWN 255
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||
|
||||
|
@ -176,6 +189,7 @@ enum flags
|
|||
XX(CB_status, "the on_status callback failed") \
|
||||
XX(CB_chunk_header, "the on_chunk_header callback failed") \
|
||||
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
|
||||
XX(CB_method, "the on_method callback failed") \
|
||||
\
|
||||
/* Parsing-related errors */ \
|
||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
||||
|
@ -218,7 +232,6 @@ enum http_errno {
|
|||
/* Get an http_errno value from an http_parser */
|
||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
||||
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
unsigned int type : 2; /* enum http_parser_type */
|
||||
|
@ -264,6 +277,7 @@ struct http_parser_settings {
|
|||
*/
|
||||
http_cb on_chunk_header;
|
||||
http_cb on_chunk_complete;
|
||||
http_data_cb on_method;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ struct message {
|
|||
enum http_method method;
|
||||
int status_code;
|
||||
char response_status[MAX_ELEMENT_SIZE];
|
||||
char request_method[MAX_ELEMENT_SIZE];
|
||||
char request_path[MAX_ELEMENT_SIZE];
|
||||
char request_url[MAX_ELEMENT_SIZE];
|
||||
char fragment[MAX_ELEMENT_SIZE];
|
||||
|
@ -103,6 +104,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/test"
|
||||
|
@ -134,6 +136,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/favicon.ico"
|
||||
|
@ -163,6 +166,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/dumbfuck"
|
||||
|
@ -184,6 +188,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= "page=1"
|
||||
,.fragment= "posts-17408"
|
||||
,.request_path= "/forums/1/topics/2375"
|
||||
|
@ -203,6 +208,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/get_no_headers_no_body/world"
|
||||
|
@ -222,6 +228,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/get_one_header_no_body"
|
||||
|
@ -245,6 +252,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 0
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/get_funky_content_length_body_hello"
|
||||
|
@ -270,6 +278,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_POST
|
||||
,.request_method = "POST"
|
||||
,.query_string= "q=search"
|
||||
,.fragment= "hey"
|
||||
,.request_path= "/post_identity_body_world"
|
||||
|
@ -297,6 +306,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_POST
|
||||
,.request_method = "POST"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/post_chunked_all_your_base"
|
||||
|
@ -325,6 +335,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_POST
|
||||
,.request_method = "POST"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/two_chunks_mult_zero_end"
|
||||
|
@ -355,6 +366,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_POST
|
||||
,.request_method = "POST"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/chunked_w_trailing_headers"
|
||||
|
@ -385,6 +397,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_POST
|
||||
,.request_method = "POST"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/chunked_w_bullshit_after_length"
|
||||
|
@ -407,6 +420,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= "foo=\"bar\""
|
||||
,.fragment= ""
|
||||
,.request_path= "/with_\"stupid\"_quotes"
|
||||
|
@ -433,6 +447,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 0
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/test"
|
||||
|
@ -456,6 +471,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= "foo=bar?baz"
|
||||
,.fragment= ""
|
||||
,.request_path= "/test.cgi"
|
||||
|
@ -477,6 +493,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/test"
|
||||
|
@ -504,6 +521,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/demo"
|
||||
|
@ -535,6 +553,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 0
|
||||
,.method= HTTP_CONNECT
|
||||
,.request_method = "CONNECT"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= ""
|
||||
|
@ -557,6 +576,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_REPORT
|
||||
,.request_method = "REPORT"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/test"
|
||||
|
@ -576,6 +596,7 @@ const struct message requests[] =
|
|||
,.http_major= 0
|
||||
,.http_minor= 9
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/"
|
||||
|
@ -598,6 +619,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_MSEARCH
|
||||
,.request_method = "M-SEARCH"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "*"
|
||||
|
@ -633,6 +655,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/"
|
||||
|
@ -658,6 +681,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= "hail=all"
|
||||
,.fragment= ""
|
||||
,.request_path= ""
|
||||
|
@ -678,6 +702,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= "hail=all"
|
||||
,.fragment= ""
|
||||
,.request_path= ""
|
||||
|
@ -699,6 +724,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= ""
|
||||
|
@ -725,6 +751,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_PATCH
|
||||
,.request_method = "PATCH"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/file.txt"
|
||||
|
@ -750,6 +777,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 0
|
||||
,.method= HTTP_CONNECT
|
||||
,.request_method = "CONNECT"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= ""
|
||||
|
@ -774,6 +802,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= "q=1"
|
||||
,.fragment= "narf"
|
||||
,.request_path= "/δ¶/δt/pope"
|
||||
|
@ -796,6 +825,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 0
|
||||
,.method= HTTP_CONNECT
|
||||
,.request_method = "CONNECT"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= ""
|
||||
|
@ -823,6 +853,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_POST
|
||||
,.request_method = "POST"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/"
|
||||
|
@ -851,6 +882,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_POST
|
||||
,.request_method = "POST"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/"
|
||||
|
@ -876,6 +908,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_PURGE
|
||||
,.request_method = "PURGE"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/file.txt"
|
||||
|
@ -896,6 +929,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_SEARCH
|
||||
,.request_method = "SEARCH"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/"
|
||||
|
@ -915,6 +949,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.fragment= ""
|
||||
,.request_path= "/toto"
|
||||
,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
|
||||
|
@ -949,6 +984,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/"
|
||||
|
@ -982,6 +1018,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/demo"
|
||||
|
@ -1012,6 +1049,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/demo"
|
||||
|
@ -1037,6 +1075,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_GET
|
||||
,.request_method = "GET"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/demo"
|
||||
|
@ -1065,6 +1104,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_POST
|
||||
,.request_method = "POST"
|
||||
,.request_path= "/demo"
|
||||
,.request_url= "/demo"
|
||||
,.num_headers= 4
|
||||
|
@ -1091,6 +1131,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 0
|
||||
,.method= HTTP_CONNECT
|
||||
,.request_method = "CONNECT"
|
||||
,.request_url= "foo.bar.com:443"
|
||||
,.num_headers= 3
|
||||
,.upgrade="blarfcicle"
|
||||
|
@ -1118,6 +1159,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_LINK
|
||||
,.request_method = "LINK"
|
||||
,.request_path= "/images/my_dog.jpg"
|
||||
,.request_url= "/images/my_dog.jpg"
|
||||
,.query_string= ""
|
||||
|
@ -1142,6 +1184,7 @@ const struct message requests[] =
|
|||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_UNLINK
|
||||
,.request_method = "UNLINK"
|
||||
,.request_path= "/images/my_dog.jpg"
|
||||
,.request_url= "/images/my_dog.jpg"
|
||||
,.query_string= ""
|
||||
|
@ -1153,6 +1196,30 @@ const struct message requests[] =
|
|||
,.body= ""
|
||||
}
|
||||
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
#define UNKNOWN_METHOD 42
|
||||
, {.name="unknown method"
|
||||
,.type= HTTP_REQUEST
|
||||
,.raw= "NON-STANDARD-METHOD / HTTP/1.1\r\n"
|
||||
"Host: example.com\r\n"
|
||||
"\r\n"
|
||||
,.should_keep_alive= TRUE
|
||||
,.message_complete_on_eof= FALSE
|
||||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_METHOD_UNKNOWN
|
||||
,.request_method= "NON-STANDARD-METHOD"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.request_path= "/"
|
||||
,.request_url= "/"
|
||||
,.num_headers= 1
|
||||
,.headers=
|
||||
{ { "Host", "example.com" } }
|
||||
,.body= ""
|
||||
}
|
||||
#endif
|
||||
|
||||
, {.name= NULL } /* sentinel */
|
||||
};
|
||||
|
||||
|
@ -1815,6 +1882,19 @@ strlcpy(char *dst, const char *src, size_t len)
|
|||
return strlncpy(dst, len, src, (size_t) -1);
|
||||
}
|
||||
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
int
|
||||
request_method_cb(http_parser *p, const char *buf, size_t len)
|
||||
{
|
||||
assert(p == parser);
|
||||
strlncat(messages[num_messages].request_method,
|
||||
sizeof(messages[num_messages].request_method),
|
||||
buf,
|
||||
len);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
request_url_cb (http_parser *p, const char *buf, size_t len)
|
||||
{
|
||||
|
@ -2117,6 +2197,16 @@ pause_header_value_cb (http_parser *p, const char *buf, size_t len)
|
|||
return header_value_cb(p, buf, len);
|
||||
}
|
||||
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
int
|
||||
pause_request_method_cb (http_parser *p, const char *buf, size_t len)
|
||||
{
|
||||
http_parser_pause(p, 1);
|
||||
*current_pause_parser = settings_dontcall;
|
||||
return request_method_cb(p, buf, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
pause_request_url_cb (http_parser *p, const char *buf, size_t len)
|
||||
{
|
||||
|
@ -2198,6 +2288,9 @@ static http_parser_settings settings_pause =
|
|||
,.on_message_complete = pause_message_complete_cb
|
||||
,.on_chunk_header = pause_chunk_header_cb
|
||||
,.on_chunk_complete = pause_chunk_complete_cb
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
,.on_method = pause_request_method_cb
|
||||
#endif
|
||||
};
|
||||
|
||||
static http_parser_settings settings =
|
||||
|
@ -2211,6 +2304,9 @@ static http_parser_settings settings =
|
|||
,.on_message_complete = message_complete_cb
|
||||
,.on_chunk_header = chunk_header_cb
|
||||
,.on_chunk_complete = chunk_complete_cb
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
,.on_method = request_method_cb
|
||||
#endif
|
||||
};
|
||||
|
||||
static http_parser_settings settings_count_body =
|
||||
|
@ -2224,6 +2320,9 @@ static http_parser_settings settings_count_body =
|
|||
,.on_message_complete = message_complete_cb
|
||||
,.on_chunk_header = chunk_header_cb
|
||||
,.on_chunk_complete = chunk_complete_cb
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
,.on_method = request_method_cb
|
||||
#endif
|
||||
};
|
||||
|
||||
static http_parser_settings settings_connect =
|
||||
|
@ -2237,6 +2336,9 @@ static http_parser_settings settings_connect =
|
|||
,.on_message_complete = connect_message_complete_cb
|
||||
,.on_chunk_header = chunk_header_cb
|
||||
,.on_chunk_complete = chunk_complete_cb
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
,.on_method = request_method_cb
|
||||
#endif
|
||||
};
|
||||
|
||||
static http_parser_settings settings_null =
|
||||
|
@ -2250,6 +2352,9 @@ static http_parser_settings settings_null =
|
|||
,.on_message_complete = 0
|
||||
,.on_chunk_header = 0
|
||||
,.on_chunk_complete = 0
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
,.on_method = 0
|
||||
#endif
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -2376,6 +2481,9 @@ message_eq (int index, int connect, const struct message *expected)
|
|||
|
||||
if (expected->type == HTTP_REQUEST) {
|
||||
MESSAGE_CHECK_NUM_EQ(expected, m, method);
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
MESSAGE_CHECK_STR_EQ(expected, m, request_method);
|
||||
#endif
|
||||
} else {
|
||||
MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
|
||||
MESSAGE_CHECK_STR_EQ(expected, m, response_status);
|
||||
|
@ -3850,11 +3958,10 @@ test_message_connect (const struct message *msg)
|
|||
{
|
||||
char *buf = (char*) msg->raw;
|
||||
size_t buflen = strlen(msg->raw);
|
||||
size_t nread;
|
||||
|
||||
parser_init(msg->type);
|
||||
|
||||
nread = parse_connect(buf, buflen);
|
||||
parse_connect(buf, buflen);
|
||||
|
||||
if (num_messages != 1) {
|
||||
printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
|
||||
|
@ -4074,7 +4181,15 @@ main (void)
|
|||
for (this_method = bad_methods; *this_method; this_method++) {
|
||||
char buf[200];
|
||||
sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
|
||||
#if HTTP_PARSER_METHOD_CB
|
||||
if (strchr(*this_method, ' ')) {
|
||||
test_simple(buf, HPE_INVALID_URL);
|
||||
} else {
|
||||
test_simple(buf, HPE_OK);
|
||||
}
|
||||
#else
|
||||
test_simple(buf, HPE_INVALID_METHOD);
|
||||
#endif
|
||||
}
|
||||
|
||||
// illegal header field name line folding
|
||||
|
|
Loading…
Reference in New Issue