common,server: handle bracketed IPv6 literals in URL authority (#25140)
* common,server: handle bracketed IPv6 literals in URL authority Parse the [host]:port form (RFC 3986) and bracket IPv6 hosts when formatting a URL authority: listening log, proxy Host header, proxy log, client rebuild. The per-request remote_addr stays bare. * common: restore unsupported scheme throw in url parser Address @ngxson review: keep the explicit reject in port resolution so the block stays self-contained. Non-http(s) schemes still throw (also gated at the top of common_http_parse_url).
This commit is contained in:
+26
-4
@@ -11,6 +11,11 @@ struct common_http_url {
|
|||||||
std::string path;
|
std::string path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// bracket an IPv6 literal host for a URL authority (RFC 3986)
|
||||||
|
static std::string common_http_format_host(const std::string & host) {
|
||||||
|
return host.find(':') != std::string::npos ? "[" + host + "]" : host;
|
||||||
|
}
|
||||||
|
|
||||||
static common_http_url common_http_parse_url(const std::string & url) {
|
static common_http_url common_http_parse_url(const std::string & url) {
|
||||||
common_http_url parts;
|
common_http_url parts;
|
||||||
auto scheme_end = url.find("://");
|
auto scheme_end = url.find("://");
|
||||||
@@ -49,11 +54,28 @@ static common_http_url common_http_parse_url(const std::string & url) {
|
|||||||
parts.path = "/";
|
parts.path = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// split the authority into host and optional port, a bracketed IPv6 literal keeps its inner colons (RFC 3986)
|
||||||
|
std::string port_str;
|
||||||
|
if (!parts.host.empty() && parts.host.front() == '[') {
|
||||||
|
auto close = parts.host.find(']');
|
||||||
|
if (close == std::string::npos) {
|
||||||
|
throw std::runtime_error("invalid IPv6 URL authority: " + parts.host);
|
||||||
|
}
|
||||||
|
auto after = parts.host.substr(close + 1);
|
||||||
|
if (!after.empty() && after.front() == ':') {
|
||||||
|
port_str = after.substr(1);
|
||||||
|
}
|
||||||
|
parts.host = parts.host.substr(1, close - 1);
|
||||||
|
} else {
|
||||||
auto colon_pos = parts.host.find(':');
|
auto colon_pos = parts.host.find(':');
|
||||||
|
|
||||||
if (colon_pos != std::string::npos) {
|
if (colon_pos != std::string::npos) {
|
||||||
parts.port = std::stoi(parts.host.substr(colon_pos + 1));
|
port_str = parts.host.substr(colon_pos + 1);
|
||||||
parts.host = parts.host.substr(0, colon_pos);
|
parts.host = parts.host.substr(0, colon_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!port_str.empty()) {
|
||||||
|
parts.port = std::stoi(port_str);
|
||||||
} else if (parts.scheme == "http") {
|
} else if (parts.scheme == "http") {
|
||||||
parts.port = 80;
|
parts.port = 80;
|
||||||
} else if (parts.scheme == "https") {
|
} else if (parts.scheme == "https") {
|
||||||
@@ -83,7 +105,7 @@ static std::pair<httplib::Client, common_http_url> common_http_client(const std:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
httplib::Client cli(parts.scheme + "://" + parts.host + ":" + std::to_string(parts.port));
|
httplib::Client cli(parts.scheme + "://" + common_http_format_host(parts.host) + ":" + std::to_string(parts.port));
|
||||||
|
|
||||||
if (!parts.user.empty()) {
|
if (!parts.user.empty()) {
|
||||||
cli.set_basic_auth(parts.user, parts.password);
|
cli.set_basic_auth(parts.user, parts.password);
|
||||||
@@ -95,5 +117,5 @@ static std::pair<httplib::Client, common_http_url> common_http_client(const std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string common_http_show_masked_url(const common_http_url & parts) {
|
static std::string common_http_show_masked_url(const common_http_url & parts) {
|
||||||
return parts.scheme + "://" + (parts.user.empty() ? "" : "****:****@") + parts.host + parts.path;
|
return parts.scheme + "://" + (parts.user.empty() ? "" : "****:****@") + common_http_format_host(parts.host) + parts.path;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ static server_http_res_ptr proxy_request(const server_http_req & req, std::strin
|
|||||||
throw std::runtime_error("unsupported URL scheme in target URL: " + parsed_url.scheme);
|
throw std::runtime_error("unsupported URL scheme in target URL: " + parsed_url.scheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
SRV_INF("proxying %s request to %s://%s:%i%s\n", method.c_str(), parsed_url.scheme.c_str(), parsed_url.host.c_str(), parsed_url.port, parsed_url.path.c_str());
|
SRV_INF("proxying %s request to %s://%s:%i%s\n", method.c_str(), parsed_url.scheme.c_str(), common_http_format_host(parsed_url.host).c_str(), parsed_url.port, parsed_url.path.c_str());
|
||||||
|
|
||||||
std::map<std::string, std::string> headers;
|
std::map<std::string, std::string> headers;
|
||||||
const std::string proxy_header_prefix = "x-llama-server-proxy-header-";
|
const std::string proxy_header_prefix = "x-llama-server-proxy-header-";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "http.h"
|
||||||
#include "server-http.h"
|
#include "server-http.h"
|
||||||
#include "server-stream.h"
|
#include "server-stream.h"
|
||||||
#include "server-common.h"
|
#include "server-common.h"
|
||||||
@@ -441,7 +442,7 @@ bool server_http_context::start() {
|
|||||||
srv->wait_until_ready();
|
srv->wait_until_ready();
|
||||||
|
|
||||||
listening_address = is_sock ? string_format("unix://%s", hostname.c_str())
|
listening_address = is_sock ? string_format("unix://%s", hostname.c_str())
|
||||||
: string_format("%s://%s:%d", is_ssl ? "https" : "http", hostname.c_str(), port);
|
: string_format("%s://%s:%d", is_ssl ? "https" : "http", common_http_format_host(hostname).c_str(), port);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "server-common.h"
|
#include "server-common.h"
|
||||||
|
#include "http.h"
|
||||||
#include "server-models.h"
|
#include "server-models.h"
|
||||||
#include "server-context.h"
|
#include "server-context.h"
|
||||||
#include "server-stream.h"
|
#include "server-stream.h"
|
||||||
@@ -2263,7 +2264,8 @@ server_http_proxy::server_http_proxy(
|
|||||||
}
|
}
|
||||||
if (lowered == "host") {
|
if (lowered == "host") {
|
||||||
bool is_default_port = (scheme == "https" && port == 443) || (scheme == "http" && port == 80);
|
bool is_default_port = (scheme == "https" && port == 443) || (scheme == "http" && port == 80);
|
||||||
req.set_header(key, is_default_port ? host : host + ":" + std::to_string(port));
|
const std::string url_host = common_http_format_host(host);
|
||||||
|
req.set_header(key, is_default_port ? url_host : url_host + ":" + std::to_string(port));
|
||||||
} else {
|
} else {
|
||||||
req.set_header(key, value);
|
req.set_header(key, value);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user