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) {
common_http_url parts;
auto scheme_end = url.find("://");
parts.path = "/";
}
- auto colon_pos = parts.host.find(':');
+ // 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(':');
+ if (colon_pos != std::string::npos) {
+ port_str = parts.host.substr(colon_pos + 1);
+ parts.host = parts.host.substr(0, colon_pos);
+ }
+ }
- if (colon_pos != std::string::npos) {
- parts.port = std::stoi(parts.host.substr(colon_pos + 1));
- parts.host = parts.host.substr(0, colon_pos);
+ if (!port_str.empty()) {
+ parts.port = std::stoi(port_str);
} else if (parts.scheme == "http") {
parts.port = 80;
} else if (parts.scheme == "https") {
}
#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()) {
cli.set_basic_auth(parts.user, parts.password);
}
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;
}
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;
const std::string proxy_header_prefix = "x-llama-server-proxy-header-";
#include "common.h"
+#include "http.h"
#include "server-http.h"
#include "server-stream.h"
#include "server-common.h"
srv->wait_until_ready();
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;
}
#include "server-common.h"
+#include "http.h"
#include "server-models.h"
#include "server-context.h"
#include "server-stream.h"
}
if (lowered == "host") {
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 {
req.set_header(key, value);
}