Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[coro_http_client][improve]update #428

Merged
merged 1 commit into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/ylt/thirdparty/cinatra/cinatra_log_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#pragma once
#include <iostream>
namespace cinatra {
struct null_logger_t {
Expand Down
47 changes: 21 additions & 26 deletions include/ylt/thirdparty/cinatra/coro_http_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<std::pair<std::string, std::string>> resp_headers;
std::span<http_header> resp_headers;
bool eof;
#ifdef BENCHMARK_TEST
uint64_t total;
Expand Down Expand Up @@ -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 {};
}
Expand All @@ -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);
Expand All @@ -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<const char *>(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) {
Expand Down Expand Up @@ -1548,20 +1556,6 @@ class coro_http_client {
co_return resp_data{{}, 200};
}

std::vector<std::pair<std::string, std::string>> get_headers(
http_parser &parser) {
std::vector<std::pair<std::string, std::string>> 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<void> async_read_ws() {
resp_data data{};
Expand Down Expand Up @@ -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_t> socket_;
Expand Down
88 changes: 38 additions & 50 deletions include/ylt/thirdparty/cinatra/http_parser.hpp
Original file line number Diff line number Diff line change
@@ -1,48 +1,62 @@
#pragma once
#include <algorithm>
#include <array>
#include <cctype>
#include <span>
#include <string>
#include <string_view>
#include <vector>

#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;
}
Expand All @@ -51,21 +65,21 @@ 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;
}

bool keep_alive() const {
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;
}

Expand All @@ -81,55 +95,29 @@ 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<phr_header *, size_t> get_headers() {
return {headers_, num_headers_};
}

void set_headers(
const std::vector<std::pair<std::string, std::string>> &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<http_header> 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;
std::string_view msg_;
size_t num_headers_ = 0;
int header_len_ = 0;
int body_len_ = 0;
struct phr_header headers_[100];
std::array<http_header, CINATRA_MAX_HTTP_HEADER_FIELD_SIZE> headers_;
};
} // namespace cinatra
Loading
Loading