@@ -99,78 +99,39 @@ class http_stream : public proxy_base
9999
100100private:
101101
102- // format a hostname (not a numeric IP) with a port for an HTTP CONNECT request.
103- // rules:
102+ // Format a hostname with port for HTTP CONNECT request per RFC 9110 Section 9.3.6.
103+ // The authority component must be in the form "host:port" where IPv6 literals
104+ // are enclosed in square brackets as defined in RFC 3986 Section 3.2.2.
105+ // Assumes host parameter contains no port suffix.
106+ // Rules:
104107 // - if port == 0, return host unchanged
105- // - if host is bracketed IPv6: [addr] or [addr]:port, append port only if missing
106- // - if host contains no ':', append ":port"
107- // - if host contains colon(s):
108- // * if suffix after last ':' is all digits, assume it's already a host:port -> leave unchanged
109- // * otherwise treat it as an (unbracketed) IPv6 literal or hostname with colons and wrap
110- // it in brackets and append :port -> [host]:port
108+ // - if host is already bracketed IPv6: [addr], append ":port"
109+ // - if host contains colons (IPv6), bracket and append ":port" -> [host]:port
110+ // - otherwise append ":port" for regular hostnames/IPv4
111111 static std::string format_host_for_connect (std::string host, unsigned short const port)
112112 {
113- if (port == 0 ) return host;
113+ // Assert that host doesn't already contain a port suffix
114+ TORRENT_ASSERT (host.empty () ||
115+ ((host.back () != ' ]' || host.find (" ]:" ) == std::string::npos) && // no [IPv6]:port
116+ (host.find (' :' ) == std::string::npos || host.find_last_of (' :' ) == host.find (' :' )))); // no host:port (unless IPv6)
114117
115- if (!host.empty () && host.front () == ' [' )
116- {
117- auto const rb = host.find (' ]' );
118- bool const has_port = (rb != std::string::npos && rb + 1 < host.size () && host[rb + 1 ] == ' :' );
119- if (!has_port) host += " :" + std::to_string (port);
120- return host;
121- }
118+ // Handle edge case: if no port specified, return host as-is
119+ if (port == 0 ) return host; // ← ไม่ใช่ ASSERT
122120
123- auto const last_colon = host. rfind ( ' : ' );
124- if (last_colon == std::string::npos )
121+ // Already bracketed IPv6 literal
122+ if (!host. empty () && host. front () == ' [ ' && host. back () == ' ] ' )
125123 {
126- host += " :" + std::to_string (port);
127- return host;
128- }
129-
130- // Check whether the suffix after the last colon is all digits
131- bool suffix_digits = last_colon + 1 < host.size ();
132- if (suffix_digits)
133- {
134- for (std::size_t i = last_colon + 1 ; i < host.size (); ++i)
135- {
136- if (!std::isdigit (static_cast <unsigned char >(host[i]))) { suffix_digits = false ; break ; }
137- }
124+ return host + " :" + std::to_string (port);
138125 }
139126
140- // If the suffix is digits, the string may be either "host:port" or an
141- // unbracketed IPv6 literal (e.g. "2001:db8::1") where the last
142- // segment happens to be numeric. Use inet_pton to detect IPv6
143- // literals:
144- // - if the whole host parses as IPv6 -> bracket and append port
145- // - else if the head (before the last colon) parses as IPv6 -> it's
146- // IPv6-with-port (leave unchanged)
147- // - otherwise keep current behavior (leave as host:port)
148- if (suffix_digits)
127+ // Contains colons (unbracketed IPv6) - need to bracket
128+ if (host.find (' :' ) != std::string::npos)
149129 {
150- in6_addr addr;
151- // whole host might be an IPv6 literal (no port)
152- if (inet_pton (AF_INET6, host.c_str (), &addr) == 1 )
153- {
154- host = " [" + host + " ]:" + std::to_string (port);
155- return host;
156- }
157-
158- // check head (before last colon) for IPv6 literal with an explicit port
159- std::string head = host.substr (0 , last_colon);
160- if (inet_pton (AF_INET6, head.c_str (), &addr) == 1 )
161- {
162- // Treat as already host:port (leave unchanged)
163- return host;
164- }
165-
166- // not IPv6; treat as host:port (leave unchanged)
167- return host;
130+ return " [" + host + " ]:" + std::to_string (port);
168131 }
169132
170- // suffix not all digits -> treat as unbracketed IPv6 or hostname with
171- // colons: bracket and append port
172- host = " [" + host + " ]:" + std::to_string (port);
173- return host;
133+ // Regular hostname or IPv4
134+ return host + " :" + std::to_string (port);
174135 }
175136
176137 template <typename Handler>
@@ -208,10 +169,16 @@ class http_stream : public proxy_base
208169 {
209170 std::string const remote_host = format_host_for_connect (m_host, m_remote_endpoint.port ());
210171 write_string (" CONNECT " + remote_host + " HTTP/1.0\r\n " , p);
172+ // Host header is required per RFC 9110 Section 7.2 and RFC 9112 Section 3.2
173+ // for HTTP/1.1 compliance, virtual host support, and proper proxy routing
174+ write_string (" Host: " + remote_host + " \r\n " , p);
211175 }
212176 else
213177 {
214178 write_string (" CONNECT " + endpoint + " HTTP/1.0\r\n " , p);
179+ // Host header is required per RFC 9110 Section 7.2 and RFC 9112 Section 3.2
180+ // for HTTP/1.1 compliance, virtual host support, and proper proxy routing
181+ write_string (" Host: " + endpoint + " \r\n " , p);
215182 }
216183 if (!m_user.empty ())
217184 {
0 commit comments