diff --git a/include/cinatra/cinatra_log_wrapper.hpp b/include/cinatra/cinatra_log_wrapper.hpp index f2d68e48..2094c40e 100644 --- a/include/cinatra/cinatra_log_wrapper.hpp +++ b/include/cinatra/cinatra_log_wrapper.hpp @@ -1,3 +1,4 @@ +#pragma once #include namespace cinatra { struct null_logger_t { diff --git a/include/cinatra/coro_http_client.hpp b/include/cinatra/coro_http_client.hpp index bce0b98d..c5fe345e 100644 --- a/include/cinatra/coro_http_client.hpp +++ b/include/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,32 @@ 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; 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 +1550,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 +1710,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/cinatra/http_parser.hpp b/include/cinatra/http_parser.hpp index 4b7ae607..8244882a 100644 --- a/include/cinatra/http_parser.hpp +++ b/include/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/cinatra/picohttpparser.h b/include/cinatra/picohttpparser.h index 29ac3777..081160ac 100644 --- a/include/cinatra/picohttpparser.h +++ b/include/cinatra/picohttpparser.h @@ -30,38 +30,21 @@ #include #include -#ifdef CINATRA_SSE -#ifdef _MSC_VER -#include -#else -#include -#endif -#endif - -#ifdef CINATRA_AVX2 -#include -#endif - -#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 */ @@ -177,31 +160,10 @@ static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, int ranges_size, int *found) { *found = 0; -#ifdef CINATRA_SSE - if (likely(buf_end - buf >= 16)) { - __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); - - size_t left = (buf_end - buf) & ~15; - do { - __m128i b16 = _mm_loadu_si128((const __m128i *)buf); - int r = _mm_cmpestri( - ranges16, ranges_size, b16, 16, - _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); - if (unlikely(r != 16)) { - buf += r; - *found = 1; - break; - } - buf += 16; - left -= 16; - } while (likely(left != 0)); - } -#else /* suppress unused parameter warning */ (void)buf_end; (void)ranges; (void)ranges_size; -#endif return buf; } @@ -209,20 +171,7 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) { const char *token_start = buf; -#ifdef CINATRA_SSE - static const char ranges1[] = - "\0\010" - /* allow HT */ - "\012\037" - /* allow SP and up to but not including DEL */ - "\177\177" - /* allow chars w. MSB set */ - ; - int found; - buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found); - if (found) - goto FOUND_CTL; -#else + /* find non-printable char within the next 8 bytes, this is the hottest code; * manually inlined */ while (likely(buf_end - buf >= 8)) { @@ -249,7 +198,7 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, } ++buf; } -#endif + for (;; ++buf) { CHECK_EOF(); if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { @@ -346,348 +295,14 @@ static const char *parse_http_version(const char *buf, const char *buf_end, return buf; } -#ifdef CINATRA_AVX2 -static unsigned long TZCNT(unsigned long long in) { - unsigned long res; - asm("tzcnt %1, %0\n\t" : "=r"(res) : "r"(in)); - return res; -} -/* Parse only 32 bytes */ -static void find_ranges32(__m256i b0, unsigned long *range0, - unsigned long *range1) { - const __m256i rr0 = _mm256_set1_epi8(0x00 - 1); - const __m256i rr1 = _mm256_set1_epi8(0x1f + 1); - const __m256i rr2 = _mm256_set1_epi8(0x3a); - const __m256i rr4 = _mm256_set1_epi8(0x7f); - const __m256i rr7 = _mm256_set1_epi8(0x09); - - /* 0<=x */ - __m256i gz0 = _mm256_cmpgt_epi8(b0, rr0); - /* 0== 96) { - b0 = _mm256_loadu_si256((void *)buf + 32 * 0); - b1 = _mm256_loadu_si256((void *)buf + 32 * 1); - b2 = _mm256_loadu_si256((void *)buf + 32 * 2); - b3 = _mm256_loadu_si256((void *)tmpbuf); - } - else if (dist >= 64) { - b0 = _mm256_loadu_si256((void *)buf + 32 * 0); - b1 = _mm256_loadu_si256((void *)buf + 32 * 1); - b2 = _mm256_loadu_si256((void *)tmpbuf); - b3 = _mm256_setzero_si256(); - } - else { - if (dist < 32) { - b0 = _mm256_loadu_si256((void *)tmpbuf); - return find_ranges32(b0, range0, range1); - } - else { - b0 = _mm256_loadu_si256((void *)buf + 32 * 0); - b1 = _mm256_loadu_si256((void *)tmpbuf); - return find_ranges64(b0, b1, range0, range1); - } - } - } - else { - /* Load 128 bytes */ - b0 = _mm256_loadu_si256((void *)buf + 32 * 0); - b1 = _mm256_loadu_si256((void *)buf + 32 * 1); - b2 = _mm256_loadu_si256((void *)buf + 32 * 2); - b3 = _mm256_loadu_si256((void *)buf + 32 * 3); - } - - /* 0<=x */ - __m256i gz0 = _mm256_cmpgt_epi8(b0, rr0); - __m256i gz1 = _mm256_cmpgt_epi8(b1, rr0); - __m256i gz2 = _mm256_cmpgt_epi8(b2, rr0); - __m256i gz3 = _mm256_cmpgt_epi8(b3, rr0); - /* 0== 65 && *buf <= 90)) { - if (!token_char_map[(unsigned char)*buf]) { - *ret = -1; - *num_headers = n_headers; - return NULL; - } - headers[n_headers].name = buf; - - /* Attempt to find a match in the index */ - found = 0; - do { - unsigned long distance = buf - prep_start; - /* Check if the bitmaps are still valid. An assumption I make is that - buf > 128 (i.e. the os will never allocate memory at address 0-128 */ - if (unlikely(distance >= - 128)) { /* Bitmaps are too old, make new ones */ - prep_start = buf; - distance = 0; - find_ranges(buf, buf_end, rr0, rr1); - } - else if (distance >= 64) { /* In the second half of the bitmap */ - unsigned long index = - rr0[1] >> (distance - 64); /* Correct offset of the bitmap */ - unsigned long find = TZCNT(index); /* Fine next set bit */ - if ((find < 64)) { /* Yey, we found a token */ - buf += find; - found = 1; - break; - } - buf = prep_start + 128; /* No token was found in the current bitmap */ - continue; - } - unsigned long index = - rr0[0] >> (distance); /* In the first half of the bitmap */ - unsigned long find = TZCNT(index); /* Find next set bit */ - if ((find < 64)) { /* Token found */ - buf += find; - found = 1; - break; - } /* Token not found, look at second half of bitmap */ - index = rr0[1]; - find = TZCNT(index); - if ((find < 64)) { - buf += 64 + find - distance; - found = 1; - break; - } - - buf = prep_start + 128; - } while (buf < buf_end); - - if (!found) - if (buf >= buf_end) { - *ret = -2; - *num_headers = n_headers; - return NULL; - } - headers[n_headers].name_len = buf - headers[n_headers].name; - ++buf; - CHECK_EOF(); - while ((*buf == ' ' || *buf == '\t')) { - ++buf; - CHECK_EOF(); - } - } - else { - headers[n_headers].name = NULL; - headers[n_headers].name_len = 0; - } - const char *token_start = buf; - - found = 0; - - do { - /* Too far */ - unsigned long distance = buf - prep_start; /* Same algorithm as above */ - if (unlikely(distance >= 128)) { - prep_start = buf; - distance = 0; - find_ranges(buf, buf_end, rr0, rr1); - } - else if (distance >= 64) { - unsigned long index = rr1[1] >> (distance - 64); - unsigned long find = TZCNT(index); - if ((find < 64)) { - buf += find; - found = 1; - break; - } - buf = prep_start + 128; - continue; - } - unsigned long index = rr1[0] >> (distance); - unsigned long find = TZCNT(index); - if ((find < 64)) { - buf += find; - found = 1; - break; - } - index = rr1[1]; - find = TZCNT(index); - if ((find < 64)) { - buf += 64 + find - distance; - found = 1; - break; - } - - buf = prep_start + 128; - } while (buf < buf_end); - - if (!found) - if (buf >= buf_end) { - *ret = -2; - *num_headers = n_headers; - return NULL; - } - - unsigned short two_char = *(unsigned short *)buf; - - if (likely(two_char == 0x0a0d)) { - headers[n_headers].value_len = buf - token_start; - buf += 2; - } - else if (unlikely(two_char & 0x0a == 0x0a)) { - headers[n_headers].value_len = buf - token_start; - ++buf; - } - else { - *ret = -1; - *num_headers = n_headers; - return NULL; - } - headers[n_headers].value = token_start; - } - *num_headers = n_headers; - return buf; -} - -#else - 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; @@ -705,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 */ @@ -731,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; } @@ -745,24 +359,23 @@ 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; } -#endif - 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) */ @@ -801,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; @@ -832,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; @@ -867,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; @@ -894,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; @@ -1059,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/cinatra/request.hpp b/include/cinatra/request.hpp index 639ddb6a..995a8787 100644 --- a/include/cinatra/request.hpp +++ b/include/cinatra/request.hpp @@ -113,7 +113,7 @@ class request { if (!copy_headers_.empty()) copy_headers_.clear(); num_headers_ = sizeof(headers_) / sizeof(headers_[0]); - header_len_ = phr_parse_request( + header_len_ = detail::phr_parse_request( buf_.data(), cur_size_, &method_, &method_len_, &url_, &url_len_, &minor_version_, headers_, &num_headers_, last_len); @@ -337,9 +337,8 @@ class request { std::string_view get_header_value(std::string_view key) const { if (copy_headers_.empty()) { for (size_t i = 0; i < num_headers_; i++) { - if (iequal(headers_[i].name, headers_[i].name_len, key.data(), - key.length())) - return std::string_view(headers_[i].value, headers_[i].value_len); + if (iequal(headers_[i].name, key)) + return headers_[i].value; } return {}; @@ -361,16 +360,14 @@ class request { return {}; } - std::pair get_headers() { + std::span get_headers() { if (copy_headers_.empty()) return {headers_, num_headers_}; num_headers_ = copy_headers_.size(); for (size_t i = 0; i < num_headers_; i++) { - headers_[i].name = copy_headers_[i].first.data(); - headers_[i].name_len = copy_headers_[i].first.size(); - headers_[i].value = copy_headers_[i].second.data(); - headers_[i].value_len = copy_headers_[i].second.size(); + headers_[i].name = copy_headers_[i].first; + headers_[i].value = copy_headers_[i].second; } return {headers_, num_headers_}; } @@ -876,9 +873,8 @@ class request { return; for (size_t i = 0; i < num_headers_; i++) { - copy_headers_.emplace_back( - std::string(headers_[i].name, headers_[i].name_len), - std::string(headers_[i].value, headers_[i].value_len)); + copy_headers_.emplace_back(std::string{headers_[i].name}, + std::string{headers_[i].value}); } } @@ -899,7 +895,7 @@ class request { std::vector buf_; size_t num_headers_ = 0; - struct phr_header headers_[64]; + http_header headers_[64]; const char *method_ = nullptr; size_t method_len_ = 0; const char *url_ = nullptr; diff --git a/include/cinatra/response.hpp b/include/cinatra/response.hpp index 86ff4460..15635a91 100644 --- a/include/cinatra/response.hpp +++ b/include/cinatra/response.hpp @@ -5,6 +5,7 @@ #ifndef CINATRA_RESPONSE_HPP #define CINATRA_RESPONSE_HPP #include +#include #include #include #include @@ -278,9 +279,7 @@ class response { std::string_view get_url() { return raw_url_; } - void set_headers(std::pair headers) { - req_headers_ = headers; - } + void set_headers(std::span headers) { req_headers_ = headers; } void render_string(std::string &&content) { #ifdef CINATRA_ENABLE_GZIP @@ -314,14 +313,10 @@ class response { private: std::string_view get_header_value(std::string_view key) const { - phr_header *headers = req_headers_.first; - size_t num_headers = req_headers_.second; - for (size_t i = 0; i < num_headers; i++) { - if (iequal(headers[i].name, headers[i].name_len, key.data(), - key.length())) - return std::string_view(headers[i].value, headers[i].value_len); + for (auto &e : req_headers_) { + if (iequal(e.name, key)) + return std::string_view(e.value); } - return {}; } @@ -335,7 +330,7 @@ class response { bool delay_ = false; - std::pair req_headers_; + std::span req_headers_; std::string_view domain_; std::string_view path_; std::shared_ptr session_ = nullptr; diff --git a/include/cinatra/utils.hpp b/include/cinatra/utils.hpp index 032924cb..8b2c35dc 100644 --- a/include/cinatra/utils.hpp +++ b/include/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(),