-
Notifications
You must be signed in to change notification settings - Fork 370
feat(cli): add native HTTPS/TLS and URL scheme parsing support #2373
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -127,6 +127,8 @@ add_executable(lemonade | |
| target_link_libraries(lemonade PRIVATE | ||
| nlohmann_json::nlohmann_json | ||
| lemonade-digest-crypto | ||
| OpenSSL::SSL | ||
| OpenSSL::Crypto | ||
| ) | ||
|
|
||
| # Link httplib based on what's available (set by parent CMakeLists.txt) | ||
|
|
@@ -177,7 +179,7 @@ if(WIN32) | |
| ) | ||
| endif() | ||
|
|
||
| target_compile_definitions(lemonade PRIVATE LEMONADE_CLI) | ||
| target_compile_definitions(lemonade PRIVATE LEMONADE_CLI CPPHTTPLIB_OPENSSL_SUPPORT) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since we already use MBEDTLS for digest verification, I'd rather not add another dependency and use |
||
|
|
||
| # Platform-specific settings | ||
| if(WIN32) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,8 +81,57 @@ const std::string& HttpError::response_body() const { | |
| return response_body_; | ||
| } | ||
|
|
||
| LemonadeClient::LemonadeClient(const std::string& host, int port, const std::string& api_key) | ||
| : host_(host), port_(port), api_key_(api_key) {} | ||
| void ParseTargetUrl(const std::string& input_host, std::string& out_clean_host, int& out_port, bool& out_is_ssl) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please let's use regular function name convention (lower_case). Also this can be a static function I think? |
||
| std::string remaining = input_host; | ||
| bool has_scheme = false; | ||
|
|
||
| if (remaining.rfind("https://", 0) == 0) { | ||
| out_is_ssl = true; | ||
| remaining = remaining.substr(8); | ||
| has_scheme = true; | ||
| } else if (remaining.rfind("http://", 0) == 0) { | ||
| out_is_ssl = false; | ||
| remaining = remaining.substr(7); | ||
| has_scheme = true; | ||
| } | ||
|
|
||
| // Strip path if present (anything starting with '/') | ||
| size_t path_pos = remaining.find('/'); | ||
| if (path_pos != std::string::npos) { | ||
| remaining = remaining.substr(0, path_pos); | ||
| } | ||
|
|
||
| // Parse port | ||
| size_t close_bracket = remaining.find(']'); | ||
| size_t colon_pos = std::string::npos; | ||
| if (remaining.rfind("[", 0) == 0 && close_bracket != std::string::npos) { | ||
| size_t post_bracket_colon = remaining.find(':', close_bracket); | ||
| if (post_bracket_colon != std::string::npos) { | ||
| colon_pos = post_bracket_colon; | ||
| } | ||
| } else { | ||
| colon_pos = remaining.rfind(':'); | ||
| } | ||
|
|
||
| if (colon_pos != std::string::npos) { | ||
| out_clean_host = remaining.substr(0, colon_pos); | ||
| std::string port_str = remaining.substr(colon_pos + 1); | ||
| try { | ||
| out_port = std::stoi(port_str); | ||
| } catch (...) { | ||
| } | ||
| } else { | ||
| out_clean_host = remaining; | ||
| if (has_scheme) { | ||
| out_port = out_is_ssl ? 443 : 80; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| LemonadeClient::LemonadeClient(const std::string& host, int port, const std::string& api_key, bool is_ssl) | ||
| : api_key_(api_key), port_(port), is_ssl_(is_ssl) { | ||
| ParseTargetUrl(host, host_, port_, is_ssl_); | ||
| } | ||
|
|
||
| LemonadeClient::~LemonadeClient() {} | ||
|
|
||
|
|
@@ -94,9 +143,16 @@ std::string LemonadeClient::normalize_host(const std::string& host) const { | |
| } | ||
|
|
||
| // Helper to create and configure httplib::Client (timeouts in milliseconds) | ||
| static httplib::Client make_client(const std::string& host, int port, const std::string& api_key, | ||
| static httplib::Client make_client(const std::string& host, int port, const std::string& api_key, bool is_ssl, | ||
| time_t connection_timeout_ms = DEFAULT_CONNECTION_TIMEOUT_MS, time_t read_timeout_ms = DEFAULT_READ_TIMEOUT_MS) { | ||
| httplib::Client cli(host, port); | ||
| #ifndef CPPHTTPLIB_OPENSSL_SUPPORT | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CPPHTTPLIB_MBEDTLS_SUPPORT |
||
| if (is_ssl) { | ||
| throw std::runtime_error("HTTPS support is not compiled in this client."); | ||
| } | ||
| #endif | ||
| std::string scheme = is_ssl ? "https" : "http"; | ||
| std::string url = scheme + "://" + host + ":" + std::to_string(port); | ||
| httplib::Client cli(url); | ||
| cli.set_connection_timeout(connection_timeout_ms / 1000, (connection_timeout_ms % 1000) * 1000); | ||
| cli.set_read_timeout(read_timeout_ms / 1000, (read_timeout_ms % 1000) * 1000); | ||
|
|
||
|
|
@@ -137,7 +193,7 @@ std::string LemonadeClient::make_request(const std::string& path, const std::str | |
| const std::string& body, const std::string& content_type, | ||
| time_t connection_timeout_ms, time_t read_timeout_ms) const { | ||
| std::string normalized_host = normalize_host(host_); | ||
| httplib::Client cli = make_client(normalized_host, port_, api_key_, connection_timeout_ms, read_timeout_ms); | ||
| httplib::Client cli = make_client(normalized_host, port_, api_key_, is_ssl_, connection_timeout_ms, read_timeout_ms); | ||
|
|
||
| httplib::Result res; | ||
|
|
||
|
|
@@ -225,7 +281,7 @@ bool LemonadeClient::make_request(const std::string& path, const std::string& me | |
| time_t connection_timeout_ms, time_t read_timeout_ms, | ||
| std::function<bool()> should_abort) const { | ||
| std::string normalized_host = normalize_host(host_); | ||
| httplib::Client cli = make_client(normalized_host, port_, api_key_, connection_timeout_ms, read_timeout_ms); | ||
| httplib::Client cli = make_client(normalized_host, port_, api_key_, is_ssl_, connection_timeout_ms, read_timeout_ms); | ||
|
|
||
| if (method == "POST") { | ||
| auto res = handle_sse_stream(cli, path, body, content_type, callback, should_abort); | ||
|
|
@@ -884,7 +940,7 @@ int LemonadeClient::list_recipes(bool show_all) const { | |
| << "-" << std::endl; | ||
| } | ||
| } else { | ||
| for (const auto& backend : recipe.backends) { | ||
| for (const auto& backend : recipe.backends) { | ||
| std::string recipe_col = first_backend ? recipe.name : ""; | ||
| std::string status_str = backend.state.empty() ? "unsupported" : backend.state; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this won't be needed if using MbedTLS