nghttp: Perform special handling of IPv6 literal with zone ID as per RFC 6874

This commit adds special handling of IPv6 literal with zone ID as per
RFC 6874.  Still IPv6 link local address does not work, since URI
parser from http-parser does not allow this syntax.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-06-18 20:00:02 +09:00
parent 25d1de0278
commit 0a6de0d378
1 changed files with 41 additions and 4 deletions

View File

@ -175,6 +175,37 @@ std::string Request::make_reqpath() const {
return path; return path;
} }
namespace {
// Perform special handling |host| if it is IPv6 literal and includes
// zone ID per RFC 6874.
std::string decode_host(std::string host) {
auto zone_start = std::find(std::begin(host), std::end(host), '%');
if (zone_start == std::end(host) ||
!util::ipv6_numeric_addr(
std::string(std::begin(host), zone_start).c_str())) {
return std::move(host);
}
// case: ::1%
if (zone_start + 1 == std::end(host)) {
host.pop_back();
return std::move(host);
}
// case: ::1%12 or ::1%1
if (zone_start + 3 >= std::end(host)) {
return std::move(host);
}
// If we see "%25", followed by more characters, then decode %25 as
// '%'.
auto zone_id_src = (*(zone_start + 1) == '2' && *(zone_start + 2) == '5')
? zone_start + 3
: zone_start + 1;
auto zone_id = util::percentDecode(zone_id_src, std::end(host));
host.erase(zone_start + 1, std::end(host));
host += zone_id;
return std::move(host);
}
} // namespace
namespace { namespace {
nghttp2_priority_spec resolve_dep(int res_type) { nghttp2_priority_spec resolve_dep(int res_type) {
nghttp2_priority_spec pri_spec; nghttp2_priority_spec pri_spec;
@ -1205,8 +1236,14 @@ void HttpClient::update_hostport() {
scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA); scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA);
std::stringstream ss; std::stringstream ss;
if (reqvec[0]->is_ipv6_literal_addr()) { if (reqvec[0]->is_ipv6_literal_addr()) {
// we may have zone ID, which must start with "%25", or "%". RFC
// 6874 defines "%25" only, and just "%" is allowed for just
// convenience to end-user input.
auto host =
util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);
auto end = std::find(std::begin(host), std::end(host), '%');
ss << "["; ss << "[";
util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); ss.write(host.c_str(), end - std::begin(host));
ss << "]"; ss << "]";
} else { } else {
util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);
@ -2269,9 +2306,9 @@ int run(char **uris, int n) {
auto port = util::has_uri_field(u, UF_PORT) auto port = util::has_uri_field(u, UF_PORT)
? u.port ? u.port
: util::get_default_port(uri.c_str(), u); : util::get_default_port(uri.c_str(), u);
auto host = decode_host(util::get_uri_field(uri.c_str(), u, UF_HOST));
if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) || if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) ||
!util::fieldeq(uri.c_str(), u, UF_HOST, prev_host.c_str()) || host != prev_host || port != prev_port) {
port != prev_port) {
if (!requests.empty()) { if (!requests.empty()) {
if (communicate(prev_scheme, prev_host, prev_port, std::move(requests), if (communicate(prev_scheme, prev_host, prev_port, std::move(requests),
callbacks) != 0) { callbacks) != 0) {
@ -2280,7 +2317,7 @@ int run(char **uris, int n) {
requests.clear(); requests.clear();
} }
prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA); prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA);
prev_host = util::get_uri_field(uri.c_str(), u, UF_HOST); prev_host = std::move(host);
prev_port = port; prev_port = port;
} }
requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd, requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd,