From 70b131caf3b30da0c400d5e656d15c9a43638c8c Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 06:59:12 +0700 Subject: [PATCH 1/8] refactor: move client implementation into core --- src/core/client.cpp | 170 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 src/core/client.cpp diff --git a/src/core/client.cpp b/src/core/client.cpp new file mode 100644 index 0000000..d7aab35 --- /dev/null +++ b/src/core/client.cpp @@ -0,0 +1,170 @@ +#include + +#include "http/request_serializer.hpp" +#include "http/response_parser.hpp" +#include "net/resolver.hpp" +#include "net/tcp_connection.hpp" + +#include +#include +#include +#include +#include + +namespace cpp_request { +namespace { + +[[nodiscard]] std::chrono::milliseconds remaining_timeout( + std::chrono::steady_clock::time_point deadline) noexcept { + const auto now = std::chrono::steady_clock::now(); + if (now >= deadline) { + return std::chrono::milliseconds{0}; + } + + auto remaining = std::chrono::duration_cast( + deadline - now); + if (remaining.count() <= 0) { + remaining = std::chrono::milliseconds{1}; + } + return remaining; +} + +[[nodiscard]] Request make_request( + Method method, + std::string_view url, + std::string_view body = {}) noexcept { + Request request{method, url}; + request.set_body(body); + return request; +} + +} // namespace + +Result Client::request(const Request& request_value) { + auto serialized_result = detail::http::serialize_request(request_value); + if (!serialized_result) { + return serialized_result.error(); + } + auto serialized = std::move(serialized_result).value(); + + auto endpoints_result = detail::net::Resolver::resolve( + serialized.url.host(), + serialized.url.port()); + if (!endpoints_result) { + return endpoints_result.error(); + } + + auto connection_result = detail::net::TcpConnection::connect( + endpoints_result.value(), + connect_timeout_); + if (!connection_result) { + return connection_result.error(); + } + auto connection = std::move(connection_result).value(); + + const auto write_deadline = std::chrono::steady_clock::now() + write_timeout_; + + auto head_write = connection.write_all( + serialized.head, + remaining_timeout(write_deadline)); + if (!head_write) { + return head_write.error(); + } + + if (!serialized.body.empty()) { + auto body_write = connection.write_all( + serialized.body, + remaining_timeout(write_deadline)); + if (!body_write) { + return body_write.error(); + } + } + + detail::http::ResponseParser parser{request_value.method()}; + std::array read_buffer{}; + + while (!parser.complete()) { + auto read_result = connection.read_some( + read_buffer.data(), + read_buffer.size(), + read_timeout_); + + if (!read_result) { + if (read_result.error().code == ErrorCode::ConnectionClosed) { + auto eof_result = parser.finish_eof(); + if (!eof_result) { + return eof_result.error(); + } + if (parser.complete()) { + break; + } + } + return read_result.error(); + } + + auto parse_result = parser.feed(std::string_view{ + read_buffer.data(), + read_result.value()}); + if (!parse_result) { + return parse_result.error(); + } + } + + return parser.take_response(); +} + +Result Client::get(std::string_view url) { + return request(make_request(Method::Get, url)); +} + +Result Client::head(std::string_view url) { + return request(make_request(Method::Head, url)); +} + +Result Client::post(std::string_view url, std::string_view body) { + return request(make_request(Method::Post, url, body)); +} + +Result Client::put(std::string_view url, std::string_view body) { + return request(make_request(Method::Put, url, body)); +} + +Result Client::patch(std::string_view url, std::string_view body) { + return request(make_request(Method::Patch, url, body)); +} + +Result Client::del(std::string_view url) { + return request(make_request(Method::Delete, url)); +} + +Result get(std::string_view url) { + Client client; + return client.get(url); +} + +Result head(std::string_view url) { + Client client; + return client.head(url); +} + +Result post(std::string_view url, std::string_view body) { + Client client; + return client.post(url, body); +} + +Result put(std::string_view url, std::string_view body) { + Client client; + return client.put(url, body); +} + +Result patch(std::string_view url, std::string_view body) { + Client client; + return client.patch(url, body); +} + +Result del(std::string_view url) { + Client client; + return client.del(url); +} + +} // namespace cpp_request From b5fe2114affadef9b69d36a6d6eb4d38157a34a4 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 06:59:20 +0700 Subject: [PATCH 2/8] refactor: move headers implementation into core --- src/core/headers.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/core/headers.cpp diff --git a/src/core/headers.cpp b/src/core/headers.cpp new file mode 100644 index 0000000..94eddaf --- /dev/null +++ b/src/core/headers.cpp @@ -0,0 +1,88 @@ +#include + +#include +#include +#include + +namespace cpp_request { +namespace { + +constexpr char ascii_lower(char ch) noexcept { + if (ch >= 'A' && ch <= 'Z') { + return static_cast(ch + ('a' - 'A')); + } + return ch; +} + +bool ascii_iequals(std::string_view lhs, std::string_view rhs) noexcept { + if (lhs.size() != rhs.size()) { + return false; + } + + for (std::size_t index = 0; index < lhs.size(); ++index) { + if (ascii_lower(lhs[index]) != ascii_lower(rhs[index])) { + return false; + } + } + return true; +} + +} // namespace + +void Headers::add(std::string_view name, std::string_view value) { + fields_.push_back(Field{std::string{name}, std::string{value}}); +} + +void Headers::set(std::string_view name, std::string_view value) { + std::string owned_name{name}; + std::string owned_value{value}; + + const auto first = std::find_if( + fields_.begin(), + fields_.end(), + [&owned_name](const Field& field) { + return ascii_iequals(field.name, owned_name); + }); + + if (first == fields_.end()) { + fields_.push_back(Field{std::move(owned_name), std::move(owned_value)}); + return; + } + + first->name = owned_name; + first->value = std::move(owned_value); + + fields_.erase( + std::remove_if( + std::next(first), + fields_.end(), + [&owned_name](const Field& field) { + return ascii_iequals(field.name, owned_name); + }), + fields_.end()); +} + +bool Headers::contains(std::string_view name) const noexcept { + return std::any_of( + fields_.begin(), + fields_.end(), + [name](const Field& field) { + return ascii_iequals(field.name, name); + }); +} + +std::string_view Headers::get(std::string_view name) const noexcept { + const auto found = std::find_if( + fields_.begin(), + fields_.end(), + [name](const Field& field) { + return ascii_iequals(field.name, name); + }); + + if (found == fields_.end()) { + return {}; + } + return found->value; +} + +} // namespace cpp_request From 7d34a3484f64bdd2e6bcbbbc17ee9a8d4a61b8c8 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 06:59:35 +0700 Subject: [PATCH 3/8] refactor: move URL implementation into core --- src/core/url.cpp | 204 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/core/url.cpp diff --git a/src/core/url.cpp b/src/core/url.cpp new file mode 100644 index 0000000..8eae0b9 --- /dev/null +++ b/src/core/url.cpp @@ -0,0 +1,204 @@ +#include + +#include +#include +#include +#include + +namespace cpp_request { +namespace { + +[[nodiscard]] bool ascii_iequals(std::string_view lhs, std::string_view rhs) noexcept { + if (lhs.size() != rhs.size()) { + return false; + } + + for (std::size_t i = 0; i < lhs.size(); ++i) { + const auto a = static_cast(lhs[i]); + const auto b = static_cast(rhs[i]); + if (std::tolower(a) != std::tolower(b)) { + return false; + } + } + return true; +} + +[[nodiscard]] bool contains_forbidden_url_byte(std::string_view input) noexcept { + for (const unsigned char ch : input) { + if (ch <= 0x20 || ch == 0x7f) { + return true; + } + } + return false; +} + +[[nodiscard]] Result parse_port(std::string_view text) noexcept { + if (text.empty()) { + return Error{ErrorCode::InvalidPort}; + } + + unsigned int value = 0; + const char* const begin = text.data(); + const char* const end = text.data() + text.size(); + const auto parsed = std::from_chars(begin, end, value, 10); + + if (parsed.ec != std::errc{} || parsed.ptr != end || value == 0 || + value > std::numeric_limits::max()) { + return Error{ErrorCode::InvalidPort}; + } + + return static_cast(value); +} + +} // namespace + +Result Url::parse(std::string_view input) { + if (input.empty() || contains_forbidden_url_byte(input)) { + return Error{ErrorCode::InvalidUrl}; + } + + const std::size_t scheme_separator = input.find("://"); + if (scheme_separator == std::string_view::npos || scheme_separator == 0) { + return Error{ErrorCode::InvalidUrl}; + } + + const std::string_view input_scheme = input.substr(0, scheme_separator); + if (!ascii_iequals(input_scheme, "http")) { + return Error{ErrorCode::UnsupportedScheme}; + } + + const std::size_t fragment = input.find('#'); + const std::string_view without_fragment = input.substr(0, fragment); + + Url result; + result.storage_.assign(without_fragment.data(), without_fragment.size()); + std::transform( + result.storage_.begin(), + result.storage_.begin() + static_cast(scheme_separator), + result.storage_.begin(), + [](unsigned char ch) { return static_cast(std::tolower(ch)); }); + + result.scheme_begin_ = 0; + result.scheme_size_ = scheme_separator; + + const std::size_t authority_begin = scheme_separator + 3; + if (authority_begin >= result.storage_.size()) { + return Error{ErrorCode::InvalidUrl}; + } + + std::size_t authority_end = result.storage_.find_first_of("/?", authority_begin); + if (authority_end == std::string::npos) { + authority_end = result.storage_.size(); + } + if (authority_end == authority_begin) { + return Error{ErrorCode::InvalidUrl}; + } + + const std::string_view authority{ + result.storage_.data() + authority_begin, + authority_end - authority_begin}; + + if (authority.find('@') != std::string_view::npos) { + return Error{ErrorCode::InvalidUrl}; + } + + if (authority.front() == '[') { + const std::size_t closing = authority.find(']'); + if (closing == std::string_view::npos || closing == 1) { + return Error{ErrorCode::InvalidUrl}; + } + + result.host_is_ipv6_literal_ = true; + result.host_begin_ = authority_begin + 1; + result.host_size_ = closing - 1; + + const std::string_view remainder = authority.substr(closing + 1); + if (!remainder.empty()) { + if (remainder.front() != ':') { + return Error{ErrorCode::InvalidUrl}; + } + auto port = parse_port(remainder.substr(1)); + if (!port) { + return port.error(); + } + result.port_ = port.value(); + result.has_explicit_port_ = true; + } + } else { + const std::size_t colon = authority.rfind(':'); + if (colon != std::string_view::npos) { + if (authority.find(':') != colon) { + return Error{ErrorCode::InvalidUrl}; + } + + const std::string_view host = authority.substr(0, colon); + if (host.empty()) { + return Error{ErrorCode::InvalidUrl}; + } + + auto port = parse_port(authority.substr(colon + 1)); + if (!port) { + return port.error(); + } + + result.host_begin_ = authority_begin; + result.host_size_ = host.size(); + result.port_ = port.value(); + result.has_explicit_port_ = true; + } else { + result.host_begin_ = authority_begin; + result.host_size_ = authority.size(); + } + } + + if (result.host_size_ == 0) { + return Error{ErrorCode::InvalidUrl}; + } + + if (authority_end == result.storage_.size()) { + result.storage_.push_back('/'); + } else if (result.storage_[authority_end] == '?') { + result.storage_.insert(authority_end, 1, '/'); + } + + const std::size_t path_begin = authority_end; + const std::size_t query_marker = result.storage_.find('?', path_begin); + + result.path_begin_ = path_begin; + if (query_marker == std::string::npos) { + result.path_size_ = result.storage_.size() - path_begin; + result.query_begin_ = result.storage_.size(); + result.query_size_ = 0; + } else { + result.path_size_ = query_marker - path_begin; + result.query_begin_ = query_marker + 1; + result.query_size_ = result.storage_.size() - result.query_begin_; + } + + result.target_begin_ = path_begin; + result.target_size_ = result.storage_.size() - path_begin; + + return result; +} + +std::string_view Url::scheme() const noexcept { + return {storage_.data() + scheme_begin_, scheme_size_}; +} + +std::string_view Url::host() const noexcept { + return {storage_.data() + host_begin_, host_size_}; +} + +std::string_view Url::path() const noexcept { + return {storage_.data() + path_begin_, path_size_}; +} + +std::string_view Url::query() const noexcept { + return {storage_.data() + query_begin_, query_size_}; +} + +std::string_view Url::target() const noexcept { + return {storage_.data() + target_begin_, target_size_}; +} + +} // namespace cpp_request From 44ba45f7e4d7fc79f06717fc5ced055fecb335c4 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 06:59:43 +0700 Subject: [PATCH 4/8] refactor: organize core implementation sources --- src/CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9da39e2..00c78c0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,8 +1,7 @@ add_library(internal - ${CMAKE_CURRENT_SOURCE_DIR}/dummy.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/url.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/headers.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/headers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/url.cpp ${CMAKE_CURRENT_SOURCE_DIR}/http/request_serializer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/http/chunked_decoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/http/response_parser.cpp From 487b820cd71c843673e18e498f0ab66416e7f9b3 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 06:59:50 +0700 Subject: [PATCH 5/8] refactor: remove relocated client source --- src/client.cpp | 170 ------------------------------------------------- 1 file changed, 170 deletions(-) delete mode 100644 src/client.cpp diff --git a/src/client.cpp b/src/client.cpp deleted file mode 100644 index d7aab35..0000000 --- a/src/client.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include - -#include "http/request_serializer.hpp" -#include "http/response_parser.hpp" -#include "net/resolver.hpp" -#include "net/tcp_connection.hpp" - -#include -#include -#include -#include -#include - -namespace cpp_request { -namespace { - -[[nodiscard]] std::chrono::milliseconds remaining_timeout( - std::chrono::steady_clock::time_point deadline) noexcept { - const auto now = std::chrono::steady_clock::now(); - if (now >= deadline) { - return std::chrono::milliseconds{0}; - } - - auto remaining = std::chrono::duration_cast( - deadline - now); - if (remaining.count() <= 0) { - remaining = std::chrono::milliseconds{1}; - } - return remaining; -} - -[[nodiscard]] Request make_request( - Method method, - std::string_view url, - std::string_view body = {}) noexcept { - Request request{method, url}; - request.set_body(body); - return request; -} - -} // namespace - -Result Client::request(const Request& request_value) { - auto serialized_result = detail::http::serialize_request(request_value); - if (!serialized_result) { - return serialized_result.error(); - } - auto serialized = std::move(serialized_result).value(); - - auto endpoints_result = detail::net::Resolver::resolve( - serialized.url.host(), - serialized.url.port()); - if (!endpoints_result) { - return endpoints_result.error(); - } - - auto connection_result = detail::net::TcpConnection::connect( - endpoints_result.value(), - connect_timeout_); - if (!connection_result) { - return connection_result.error(); - } - auto connection = std::move(connection_result).value(); - - const auto write_deadline = std::chrono::steady_clock::now() + write_timeout_; - - auto head_write = connection.write_all( - serialized.head, - remaining_timeout(write_deadline)); - if (!head_write) { - return head_write.error(); - } - - if (!serialized.body.empty()) { - auto body_write = connection.write_all( - serialized.body, - remaining_timeout(write_deadline)); - if (!body_write) { - return body_write.error(); - } - } - - detail::http::ResponseParser parser{request_value.method()}; - std::array read_buffer{}; - - while (!parser.complete()) { - auto read_result = connection.read_some( - read_buffer.data(), - read_buffer.size(), - read_timeout_); - - if (!read_result) { - if (read_result.error().code == ErrorCode::ConnectionClosed) { - auto eof_result = parser.finish_eof(); - if (!eof_result) { - return eof_result.error(); - } - if (parser.complete()) { - break; - } - } - return read_result.error(); - } - - auto parse_result = parser.feed(std::string_view{ - read_buffer.data(), - read_result.value()}); - if (!parse_result) { - return parse_result.error(); - } - } - - return parser.take_response(); -} - -Result Client::get(std::string_view url) { - return request(make_request(Method::Get, url)); -} - -Result Client::head(std::string_view url) { - return request(make_request(Method::Head, url)); -} - -Result Client::post(std::string_view url, std::string_view body) { - return request(make_request(Method::Post, url, body)); -} - -Result Client::put(std::string_view url, std::string_view body) { - return request(make_request(Method::Put, url, body)); -} - -Result Client::patch(std::string_view url, std::string_view body) { - return request(make_request(Method::Patch, url, body)); -} - -Result Client::del(std::string_view url) { - return request(make_request(Method::Delete, url)); -} - -Result get(std::string_view url) { - Client client; - return client.get(url); -} - -Result head(std::string_view url) { - Client client; - return client.head(url); -} - -Result post(std::string_view url, std::string_view body) { - Client client; - return client.post(url, body); -} - -Result put(std::string_view url, std::string_view body) { - Client client; - return client.put(url, body); -} - -Result patch(std::string_view url, std::string_view body) { - Client client; - return client.patch(url, body); -} - -Result del(std::string_view url) { - Client client; - return client.del(url); -} - -} // namespace cpp_request From 75ccc9a5cb6ebb457158d95935b47d6512c735cd Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 06:59:55 +0700 Subject: [PATCH 6/8] refactor: remove relocated headers source --- src/headers.cpp | 88 ------------------------------------------------- 1 file changed, 88 deletions(-) delete mode 100644 src/headers.cpp diff --git a/src/headers.cpp b/src/headers.cpp deleted file mode 100644 index 94eddaf..0000000 --- a/src/headers.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include - -#include -#include -#include - -namespace cpp_request { -namespace { - -constexpr char ascii_lower(char ch) noexcept { - if (ch >= 'A' && ch <= 'Z') { - return static_cast(ch + ('a' - 'A')); - } - return ch; -} - -bool ascii_iequals(std::string_view lhs, std::string_view rhs) noexcept { - if (lhs.size() != rhs.size()) { - return false; - } - - for (std::size_t index = 0; index < lhs.size(); ++index) { - if (ascii_lower(lhs[index]) != ascii_lower(rhs[index])) { - return false; - } - } - return true; -} - -} // namespace - -void Headers::add(std::string_view name, std::string_view value) { - fields_.push_back(Field{std::string{name}, std::string{value}}); -} - -void Headers::set(std::string_view name, std::string_view value) { - std::string owned_name{name}; - std::string owned_value{value}; - - const auto first = std::find_if( - fields_.begin(), - fields_.end(), - [&owned_name](const Field& field) { - return ascii_iequals(field.name, owned_name); - }); - - if (first == fields_.end()) { - fields_.push_back(Field{std::move(owned_name), std::move(owned_value)}); - return; - } - - first->name = owned_name; - first->value = std::move(owned_value); - - fields_.erase( - std::remove_if( - std::next(first), - fields_.end(), - [&owned_name](const Field& field) { - return ascii_iequals(field.name, owned_name); - }), - fields_.end()); -} - -bool Headers::contains(std::string_view name) const noexcept { - return std::any_of( - fields_.begin(), - fields_.end(), - [name](const Field& field) { - return ascii_iequals(field.name, name); - }); -} - -std::string_view Headers::get(std::string_view name) const noexcept { - const auto found = std::find_if( - fields_.begin(), - fields_.end(), - [name](const Field& field) { - return ascii_iequals(field.name, name); - }); - - if (found == fields_.end()) { - return {}; - } - return found->value; -} - -} // namespace cpp_request From a9bcdd74acd62306fba6a6931dc905857fc4bc61 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 06:59:59 +0700 Subject: [PATCH 7/8] refactor: remove relocated URL source --- src/url.cpp | 204 ---------------------------------------------------- 1 file changed, 204 deletions(-) delete mode 100644 src/url.cpp diff --git a/src/url.cpp b/src/url.cpp deleted file mode 100644 index 8eae0b9..0000000 --- a/src/url.cpp +++ /dev/null @@ -1,204 +0,0 @@ -#include - -#include -#include -#include -#include - -namespace cpp_request { -namespace { - -[[nodiscard]] bool ascii_iequals(std::string_view lhs, std::string_view rhs) noexcept { - if (lhs.size() != rhs.size()) { - return false; - } - - for (std::size_t i = 0; i < lhs.size(); ++i) { - const auto a = static_cast(lhs[i]); - const auto b = static_cast(rhs[i]); - if (std::tolower(a) != std::tolower(b)) { - return false; - } - } - return true; -} - -[[nodiscard]] bool contains_forbidden_url_byte(std::string_view input) noexcept { - for (const unsigned char ch : input) { - if (ch <= 0x20 || ch == 0x7f) { - return true; - } - } - return false; -} - -[[nodiscard]] Result parse_port(std::string_view text) noexcept { - if (text.empty()) { - return Error{ErrorCode::InvalidPort}; - } - - unsigned int value = 0; - const char* const begin = text.data(); - const char* const end = text.data() + text.size(); - const auto parsed = std::from_chars(begin, end, value, 10); - - if (parsed.ec != std::errc{} || parsed.ptr != end || value == 0 || - value > std::numeric_limits::max()) { - return Error{ErrorCode::InvalidPort}; - } - - return static_cast(value); -} - -} // namespace - -Result Url::parse(std::string_view input) { - if (input.empty() || contains_forbidden_url_byte(input)) { - return Error{ErrorCode::InvalidUrl}; - } - - const std::size_t scheme_separator = input.find("://"); - if (scheme_separator == std::string_view::npos || scheme_separator == 0) { - return Error{ErrorCode::InvalidUrl}; - } - - const std::string_view input_scheme = input.substr(0, scheme_separator); - if (!ascii_iequals(input_scheme, "http")) { - return Error{ErrorCode::UnsupportedScheme}; - } - - const std::size_t fragment = input.find('#'); - const std::string_view without_fragment = input.substr(0, fragment); - - Url result; - result.storage_.assign(without_fragment.data(), without_fragment.size()); - std::transform( - result.storage_.begin(), - result.storage_.begin() + static_cast(scheme_separator), - result.storage_.begin(), - [](unsigned char ch) { return static_cast(std::tolower(ch)); }); - - result.scheme_begin_ = 0; - result.scheme_size_ = scheme_separator; - - const std::size_t authority_begin = scheme_separator + 3; - if (authority_begin >= result.storage_.size()) { - return Error{ErrorCode::InvalidUrl}; - } - - std::size_t authority_end = result.storage_.find_first_of("/?", authority_begin); - if (authority_end == std::string::npos) { - authority_end = result.storage_.size(); - } - if (authority_end == authority_begin) { - return Error{ErrorCode::InvalidUrl}; - } - - const std::string_view authority{ - result.storage_.data() + authority_begin, - authority_end - authority_begin}; - - if (authority.find('@') != std::string_view::npos) { - return Error{ErrorCode::InvalidUrl}; - } - - if (authority.front() == '[') { - const std::size_t closing = authority.find(']'); - if (closing == std::string_view::npos || closing == 1) { - return Error{ErrorCode::InvalidUrl}; - } - - result.host_is_ipv6_literal_ = true; - result.host_begin_ = authority_begin + 1; - result.host_size_ = closing - 1; - - const std::string_view remainder = authority.substr(closing + 1); - if (!remainder.empty()) { - if (remainder.front() != ':') { - return Error{ErrorCode::InvalidUrl}; - } - auto port = parse_port(remainder.substr(1)); - if (!port) { - return port.error(); - } - result.port_ = port.value(); - result.has_explicit_port_ = true; - } - } else { - const std::size_t colon = authority.rfind(':'); - if (colon != std::string_view::npos) { - if (authority.find(':') != colon) { - return Error{ErrorCode::InvalidUrl}; - } - - const std::string_view host = authority.substr(0, colon); - if (host.empty()) { - return Error{ErrorCode::InvalidUrl}; - } - - auto port = parse_port(authority.substr(colon + 1)); - if (!port) { - return port.error(); - } - - result.host_begin_ = authority_begin; - result.host_size_ = host.size(); - result.port_ = port.value(); - result.has_explicit_port_ = true; - } else { - result.host_begin_ = authority_begin; - result.host_size_ = authority.size(); - } - } - - if (result.host_size_ == 0) { - return Error{ErrorCode::InvalidUrl}; - } - - if (authority_end == result.storage_.size()) { - result.storage_.push_back('/'); - } else if (result.storage_[authority_end] == '?') { - result.storage_.insert(authority_end, 1, '/'); - } - - const std::size_t path_begin = authority_end; - const std::size_t query_marker = result.storage_.find('?', path_begin); - - result.path_begin_ = path_begin; - if (query_marker == std::string::npos) { - result.path_size_ = result.storage_.size() - path_begin; - result.query_begin_ = result.storage_.size(); - result.query_size_ = 0; - } else { - result.path_size_ = query_marker - path_begin; - result.query_begin_ = query_marker + 1; - result.query_size_ = result.storage_.size() - result.query_begin_; - } - - result.target_begin_ = path_begin; - result.target_size_ = result.storage_.size() - path_begin; - - return result; -} - -std::string_view Url::scheme() const noexcept { - return {storage_.data() + scheme_begin_, scheme_size_}; -} - -std::string_view Url::host() const noexcept { - return {storage_.data() + host_begin_, host_size_}; -} - -std::string_view Url::path() const noexcept { - return {storage_.data() + path_begin_, path_size_}; -} - -std::string_view Url::query() const noexcept { - return {storage_.data() + query_begin_, query_size_}; -} - -std::string_view Url::target() const noexcept { - return {storage_.data() + target_begin_, target_size_}; -} - -} // namespace cpp_request From e857c9b89b57768a353275814a74e57e813cf7dc Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 07:00:04 +0700 Subject: [PATCH 8/8] refactor: remove obsolete dummy source --- src/dummy.cpp | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/dummy.cpp diff --git a/src/dummy.cpp b/src/dummy.cpp deleted file mode 100644 index 58bcfd9..0000000 --- a/src/dummy.cpp +++ /dev/null @@ -1 +0,0 @@ -void dummy() {} \ No newline at end of file