From 0ac5d15829d53a7d887c751f68131e61830ede09 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 25 Aug 2023 16:08:56 +0800 Subject: [PATCH] update --- .../cinatra/cinatra_log_wrapper.hpp | 1 + .../thirdparty/cinatra/coro_http_client.hpp | 47 +++++----- .../ylt/thirdparty/cinatra/http_parser.hpp | 88 ++++++++----------- .../ylt/thirdparty/cinatra/picohttpparser.h | 62 ++++++------- include/ylt/thirdparty/cinatra/utils.hpp | 6 ++ 5 files changed, 95 insertions(+), 109 deletions(-) diff --git a/include/ylt/thirdparty/cinatra/cinatra_log_wrapper.hpp b/include/ylt/thirdparty/cinatra/cinatra_log_wrapper.hpp index f2d68e484..2094c40ee 100644 --- a/include/ylt/thirdparty/cinatra/cinatra_log_wrapper.hpp +++ b/include/ylt/thirdparty/cinatra/cinatra_log_wrapper.hpp @@ -1,3 +1,4 @@ +#pragma once #include namespace cinatra { struct null_logger_t { diff --git a/include/ylt/thirdparty/cinatra/coro_http_client.hpp b/include/ylt/thirdparty/cinatra/coro_http_client.hpp index bce0b98de..9c7c40705 100644 --- a/include/ylt/thirdparty/cinatra/coro_http_client.hpp +++ b/include/ylt/thirdparty/cinatra/coro_http_client.hpp @@ -22,6 +22,7 @@ #include "async_simple/coro/Lazy.h" #include "cinatra_log_wrapper.hpp" #include "http_parser.hpp" +#include "picohttpparser.h" #include "response_cv.hpp" #include "uri.hpp" #include "websocket.hpp" @@ -50,11 +51,13 @@ inline ClientInjectAction inject_write_failed = ClientInjectAction::none; inline ClientInjectAction inject_read_failed = ClientInjectAction::none; #endif +struct http_header; + struct resp_data { std::error_code net_err; int status; std::string_view resp_body; - std::vector> resp_headers; + std::span resp_headers; bool eof; #ifdef BENCHMARK_TEST uint64_t total; @@ -1216,7 +1219,7 @@ class coro_http_client { return std::make_error_code(std::errc::protocol_error); } read_buf_.consume(header_size); // header size - data.resp_headers = get_headers(parser); + data.resp_headers = parser.get_headers(); data.status = parser.status(); return {}; } @@ -1234,8 +1237,7 @@ class coro_http_client { break; } - http_parser parser; - ec = handle_header(data, parser, size); + ec = handle_header(data, parser_, size); #ifdef INJECT_FOR_HTTP_CLIENT_TEST if (inject_header_valid == ClientInjectAction::header_error) { ec = std::make_error_code(std::errc::protocol_error); @@ -1248,32 +1250,38 @@ class coro_http_client { break; } - is_keep_alive = parser.keep_alive(); + is_keep_alive = parser_.keep_alive(); if (method == http_method::HEAD) { co_return data; } - bool is_ranges = parser.is_ranges(); + bool is_ranges = parser_.is_ranges(); if (is_ranges) { is_keep_alive = true; } - if (parser.is_chunked()) { + if (parser_.is_chunked()) { is_keep_alive = true; + if (read_buf_.size() > 0) { + const char *data_ptr = + asio::buffer_cast(read_buf_.data()); + chunked_buf_.sputn(data_ptr, read_buf_.size()); + read_buf_.consume(read_buf_.size()); + } ec = co_await handle_chunked(data, std::move(ctx)); break; } redirect_uri_.clear(); - bool is_redirect = parser.is_location(); + bool is_redirect = parser_.is_location(); if (is_redirect) - redirect_uri_ = parser.get_header_value("Location"); + redirect_uri_ = parser_.get_header_value("Location"); - size_t content_len = (size_t)parser.body_len(); + size_t content_len = (size_t)parser_.body_len(); #ifdef BENCHMARK_TEST - total_len_ = parser.total_len(); + total_len_ = parser_.total_len(); #endif - if ((size_t)parser.body_len() <= read_buf_.size()) { + if ((size_t)parser_.body_len() <= read_buf_.size()) { // Now get entire content, additional data will discard. // copy body. if (content_len > 0) { @@ -1548,20 +1556,6 @@ class coro_http_client { co_return resp_data{{}, 200}; } - std::vector> get_headers( - http_parser &parser) { - std::vector> resp_headers; - - auto [headers, num_headers] = parser.get_headers(); - for (size_t i = 0; i < num_headers; i++) { - resp_headers.emplace_back( - std::string(headers[i].name, headers[i].name_len), - std::string(headers[i].value, headers[i].value_len)); - } - - return resp_headers; - } - // this function must be called before async_ws_connect. async_simple::coro::Lazy async_read_ws() { resp_data data{}; @@ -1722,6 +1716,7 @@ class coro_http_client { return has_http_scheme; } + http_parser parser_; coro_io::ExecutorWrapper<> executor_wrapper_; coro_io::period_timer timer_; std::shared_ptr socket_; diff --git a/include/ylt/thirdparty/cinatra/http_parser.hpp b/include/ylt/thirdparty/cinatra/http_parser.hpp index 4b7ae6071..8244882a4 100644 --- a/include/ylt/thirdparty/cinatra/http_parser.hpp +++ b/include/ylt/thirdparty/cinatra/http_parser.hpp @@ -1,48 +1,62 @@ #pragma once +#include +#include #include +#include #include #include -#include +#include "cinatra_log_wrapper.hpp" #include "picohttpparser.h" using namespace std::string_view_literals; +#ifndef CINATRA_MAX_HTTP_HEADER_FIELD_SIZE +#define CINATRA_MAX_HTTP_HEADER_FIELD_SIZE 100 +#endif + namespace cinatra { class http_parser { public: int parse_response(const char *data, size_t size, int last_len) { int minor_version; - num_headers_ = sizeof(headers_) / sizeof(headers_[0]); + num_headers_ = CINATRA_MAX_HTTP_HEADER_FIELD_SIZE; const char *msg; size_t msg_len; - header_len_ = - phr_parse_response(data, size, &minor_version, &status_, &msg, &msg_len, - headers_, &num_headers_, last_len); + header_len_ = cinatra::detail::phr_parse_response( + data, size, &minor_version, &status_, &msg, &msg_len, headers_.data(), + &num_headers_, last_len); msg_ = {msg, msg_len}; - auto header_value = this->get_header_value("content-length"); + auto header_value = this->get_header_value("content-length"sv); if (header_value.empty()) { body_len_ = 0; } else { body_len_ = atoi(header_value.data()); } - + if (header_len_ < 0) [[unlikely]] { + CINATRA_LOG_WARNING << "parse http head failed"; + if (size == CINATRA_MAX_HTTP_HEADER_FIELD_SIZE) { + CINATRA_LOG_ERROR << "the field of http head is out of max limit " + << CINATRA_MAX_HTTP_HEADER_FIELD_SIZE + << ", you can define macro " + "CINATRA_MAX_HTTP_HEADER_FIELD_SIZE to expand it."; + } + } return header_len_; } std::string_view get_header_value(std::string_view key) const { for (size_t i = 0; i < num_headers_; i++) { - if (iequal(headers_[i].name, headers_[i].name_len, key.data())) - return std::string_view(headers_[i].value, headers_[i].value_len); + if (iequal(headers_[i].name, key)) + return headers_[i].value; } - return {}; } bool is_chunked() const { - auto transfer_encoding = this->get_header_value("transfer-encoding"); + auto transfer_encoding = this->get_header_value("transfer-encoding"sv); if (transfer_encoding == "chunked"sv) { return true; } @@ -51,12 +65,12 @@ class http_parser { } bool is_ranges() const { - auto transfer_encoding = this->get_header_value("Accept-Ranges"); + auto transfer_encoding = this->get_header_value("Accept-Ranges"sv); return !transfer_encoding.empty(); } bool is_websocket() const { - auto upgrade = this->get_header_value("Upgrade"); + auto upgrade = this->get_header_value("Upgrade"sv); return upgrade == "WebSocket"sv || upgrade == "websocket"sv; } @@ -64,8 +78,8 @@ class http_parser { if (is_websocket()) { return true; } - auto val = this->get_header_value("connection"); - if (val.empty() || iequal(val.data(), val.length(), "keep-alive")) { + auto val = this->get_header_value("connection"sv); + if (val.empty() || iequal(val, "keep-alive"sv)) { return true; } @@ -81,48 +95,22 @@ class http_parser { int total_len() const { return header_len_ + body_len_; } bool is_location() { - auto location = this->get_header_value("Location"); + auto location = this->get_header_value("Location"sv); return !location.empty(); } std::string_view msg() const { return msg_; } - std::pair get_headers() { - return {headers_, num_headers_}; - } - - void set_headers( - const std::vector> &headers) { - num_headers_ = headers.size(); - for (size_t i = 0; i < num_headers_; i++) { - headers_[i].name = headers[i].first.data(); - headers_[i].name_len = headers[i].first.size(); - headers_[i].value = headers[i].second.data(); - headers_[i].value_len = headers[i].second.size(); - } + std::span get_headers() { + return {headers_.data(), num_headers_}; } private: - std::string_view get_header_value(phr_header *headers, size_t num_headers, - std::string_view key) { - for (size_t i = 0; i < num_headers; i++) { - if (iequal(headers[i].name, headers[i].name_len, key.data())) - return std::string_view(headers[i].value, headers[i].value_len); - } - - return {}; - } - - bool iequal(const char *s, size_t l, const char *t) const { - if (strlen(t) != l) - return false; - - for (size_t i = 0; i < l; i++) { - if (std::tolower(s[i]) != std::tolower(t[i])) - return false; - } - - return true; + bool iequal(std::string_view a, std::string_view b) const { + return std::equal(a.begin(), a.end(), b.begin(), b.end(), + [](char a, char b) { + return tolower(a) == tolower(b); + }); } int status_ = 0; @@ -130,6 +118,6 @@ class http_parser { size_t num_headers_ = 0; int header_len_ = 0; int body_len_ = 0; - struct phr_header headers_[100]; + std::array headers_; }; } // namespace cinatra \ No newline at end of file diff --git a/include/ylt/thirdparty/cinatra/picohttpparser.h b/include/ylt/thirdparty/cinatra/picohttpparser.h index 19b81ded7..081160ac5 100644 --- a/include/ylt/thirdparty/cinatra/picohttpparser.h +++ b/include/ylt/thirdparty/cinatra/picohttpparser.h @@ -30,24 +30,21 @@ #include #include +#include + #ifdef _MSC_VER #define ssize_t intptr_t #endif -/* $Id: 67fd3ee74103ada60258d8a16e868f483abcca87 $ */ - -#ifdef __cplusplus -extern "C" { -#endif +namespace cinatra { +struct http_header { + std::string_view name; + std::string_view value; +}; +namespace detail { /* contains name and value of a header (name == NULL if is a continuing line * of a multiline header */ -struct phr_header { - const char *name; - size_t name_len; - const char *value; - size_t value_len; -}; /* returns number of bytes consumed if successful, -2 if request is partial, * -1 if failed */ @@ -299,10 +296,13 @@ static const char *parse_http_version(const char *buf, const char *buf_end, } static const char *parse_headers(const char *buf, const char *buf_end, - struct phr_header *headers, - size_t *num_headers, size_t max_headers, - int *ret) { + http_header *headers, size_t *num_headers, + size_t max_headers, int *ret) { for (;; ++*num_headers) { + const char *name; + size_t name_len; + const char *value; + size_t value_len; CHECK_EOF(); if (*buf == '\015') { ++buf; @@ -320,7 +320,7 @@ static const char *parse_headers(const char *buf, const char *buf_end, if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { /* parsing name, but do not discard SP before colon, see * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ - headers[*num_headers].name = buf; + name = buf; static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */ "\"\"" /* 0x22 */ @@ -346,8 +346,7 @@ static const char *parse_headers(const char *buf, const char *buf_end, ++buf; CHECK_EOF(); } - if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == - 0) { + if ((name_len = buf - name) == 0) { *ret = -1; return NULL; } @@ -360,14 +359,15 @@ static const char *parse_headers(const char *buf, const char *buf_end, } } else { - headers[*num_headers].name = NULL; - headers[*num_headers].name_len = 0; + name = NULL; + name_len = 0; } - if ((buf = get_token_to_eol(buf, buf_end, &headers[*num_headers].value, - &headers[*num_headers].value_len, ret)) == + if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) { return NULL; } + headers[*num_headers] = {std::string_view{name, name_len}, + std::string_view{value, value_len}}; } return buf; } @@ -375,7 +375,7 @@ static const char *parse_headers(const char *buf, const char *buf_end, static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, size_t *path_len, - int *minor_version, struct phr_header *headers, + int *minor_version, http_header *headers, size_t *num_headers, size_t max_headers, int *ret) { /* skip first empty line (some clients add CRLF after POST content) */ @@ -414,7 +414,7 @@ static const char *parse_request(const char *buf, const char *buf_end, inline int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, - int *minor_version, struct phr_header *headers, + int *minor_version, http_header *headers, size_t *num_headers, size_t last_len) { const char *buf = buf_start, *buf_end = buf_start + len; size_t max_headers = *num_headers; @@ -445,9 +445,8 @@ inline int phr_parse_request(const char *buf_start, size_t len, inline const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg, size_t *msg_len, - struct phr_header *headers, - size_t *num_headers, size_t max_headers, - int *ret) { + http_header *headers, size_t *num_headers, + size_t max_headers, int *ret) { /* parse "HTTP/1.x" */ if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { return NULL; @@ -480,7 +479,7 @@ inline const char *parse_response(const char *buf, const char *buf_end, inline int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, - size_t *msg_len, struct phr_header *headers, + size_t *msg_len, http_header *headers, size_t *num_headers, size_t last_len) { const char *buf = buf_start, *buf_end = buf + len; size_t max_headers = *num_headers; @@ -507,7 +506,7 @@ inline int phr_parse_response(const char *buf_start, size_t len, } inline int phr_parse_headers(const char *buf_start, size_t len, - struct phr_header *headers, size_t *num_headers, + http_header *headers, size_t *num_headers, size_t last_len) { const char *buf = buf_start, *buf_end = buf + len; size_t max_headers = *num_headers; @@ -672,13 +671,10 @@ inline ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, inline int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) { return decoder->_state == CHUNKED_IN_CHUNK_DATA; } - +} // namespace detail +} // namespace cinatra #undef CHECK_EOF #undef EXPECT_CHAR #undef ADVANCE_TOKEN -#ifdef __cplusplus -} -#endif - #endif diff --git a/include/ylt/thirdparty/cinatra/utils.hpp b/include/ylt/thirdparty/cinatra/utils.hpp index 032924cb2..8b2c35dcd 100644 --- a/include/ylt/thirdparty/cinatra/utils.hpp +++ b/include/ylt/thirdparty/cinatra/utils.hpp @@ -353,6 +353,12 @@ inline bool iequal(const char *s, size_t l, const char *t, size_t size) { return true; } +inline bool iequal(std::string_view a, std::string_view b) { + return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { + return tolower(a) == tolower(b); + }); +} + template inline bool find_strIC(const T &src, const T &dest) { auto it = std::search(src.begin(), src.end(), dest.begin(), dest.end(),