Skip to content
Open
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
27 changes: 27 additions & 0 deletions include/seastar/http/request.hh
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,28 @@ public:
*/
static request make(httpd::operation_type type, sstring host, sstring path);

/**
* \brief Make simple request to an address
*
* \param method - method to use, e.g. "GET"
* \param addr - the address to contact. The "Host" header is its RFC 3986
* authority form, i.e. an IPv6 address is bracketed.
* \path - the URL to send the request to
*
*/
static request make(sstring method, const socket_address& addr, sstring path);

/**
* \brief Make simple request to an address
*
* \param method - method to use, e.g. operation_type::GET
* \param addr - the address to contact. The "Host" header is its RFC 3986
* authority form, i.e. an IPv6 address is bracketed.
* \path - the URL to send the request to
*
*/
static request make(httpd::operation_type type, const socket_address& addr, sstring path);

sstring request_line() const;
future<> write_request_headers(output_stream<char>& out) const;
private:
Expand All @@ -430,6 +452,11 @@ private:
};

namespace internal {

/// The RFC 3986 §3.2.2 authority for \param addr: "host:port", with an IPv6
/// address bracketed. A zone index is not part of an authority and is dropped.
sstring format_authority(const socket_address& addr);

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
inline sstring& deprecated_content(request& req) noexcept { return req.content; }
Expand Down
19 changes: 19 additions & 0 deletions src/http/request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <utility>

#include <seastar/http/request.hh>
#include <seastar/net/inet_address.hh>
#include <seastar/http/url.hh>
#include <seastar/http/common.hh>
#include <seastar/util/assert.hh>
Expand Down Expand Up @@ -168,5 +169,23 @@ request request::make(httpd::operation_type type, sstring host, sstring path) {
return make(httpd::type2str(type), std::move(host), std::move(path));
}

request request::make(sstring method, const socket_address& addr, sstring path) {
return make(std::move(method), internal::format_authority(addr), std::move(path));
}

request request::make(httpd::operation_type type, const socket_address& addr, sstring path) {
return make(httpd::type2str(type), internal::format_authority(addr), std::move(path));
}

sstring internal::format_authority(const socket_address& addr) {
auto ip = addr.addr();
if (ip.is_ipv6()) {
// inet_address prints a scope as "%<index>", which an authority cannot
// carry; build the address without one.
return format("[{}]:{:d}", net::inet_address(static_cast<::in6_addr>(ip)), addr.port());
}
return format("{}:{:d}", ip, addr.port());
}

} // http namespace
} // seastar namespace
16 changes: 16 additions & 0 deletions tests/unit/httpd_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2434,6 +2434,22 @@ BOOST_AUTO_TEST_CASE(test_http_status_classification) {
BOOST_REQUIRE_EQUAL(unclassified, 300);
}

SEASTAR_TEST_CASE(test_format_authority) {
BOOST_REQUIRE_EQUAL(http::internal::format_authority(socket_address(ipv4_addr("10.0.0.1", 80))), "10.0.0.1:80");
BOOST_REQUIRE_EQUAL(http::internal::format_authority(socket_address(ipv6_addr("2001:db8::1", 8080))), "[2001:db8::1]:8080");
BOOST_REQUIRE_EQUAL(http::internal::format_authority(socket_address(ipv6_addr("::1", 0))), "[::1]:0");
// a zone belongs to the host, not to the authority
BOOST_REQUIRE_EQUAL(http::internal::format_authority(socket_address(ipv6_addr("fe80::1", 8080), 2)), "[fe80::1]:8080");

// and the request overload puts exactly that in the Host header
auto v6 = http::request::make("GET", socket_address(ipv6_addr("::1", 8080)), "/test");
BOOST_REQUIRE_EQUAL(v6.get_header("Host"), "[::1]:8080");
BOOST_REQUIRE_EQUAL(v6.get_url(), "http://[::1]:8080/test");
auto v4 = http::request::make(GET, socket_address(ipv4_addr("127.0.0.1", 80)), "/test");
BOOST_REQUIRE_EQUAL(v4.get_header("Host"), "127.0.0.1:80");
return make_ready_future();
}

// An IPv4 client of a `[::]` listener is an IPv4 client to the handler, not the
// ::ffff:a.b.c.d the kernel reports.
SEASTAR_THREAD_TEST_CASE(test_dual_stack_listener_reports_ipv4_client) {
Expand Down
Loading