Proxy: Only use HTTP CONNECT for HTTPS requests

Previously, `ureq` used `HTTP CONNECT` for *all* requests, including
plain-text HTTP requests. However, some proxy servers like Squid
only allow tunneling via the `HTTP CONNECT` method on port 443 - since
it is usually only used to proxy HTTPS requests. As a result,
it was not possible to use `ureq` with a Squid server in its default configuration.

With the changes from this commit, ureq will interact with HTTP proxies
in a more standard-conforming way, where `CONNECT` is only used for
HTTPS requests. HTTP request paths are transformed in such a way that they
comply with RFC 7230 [1].

Tested against Squid 4.13 in standard configuration, with and without basic authentication,
for HTTP and HTTPS requests.

[1] https://www.rfc-editor.org/rfc/rfc7230#section-5.3.2
This commit is contained in:
Lukas Wagner
2023-01-30 11:31:35 +01:00
committed by Martin Algesten
parent bdcee72c53
commit 20a9ae7977
5 changed files with 31 additions and 18 deletions

View File

@@ -15,6 +15,7 @@ use crate::body::{self, BodySize, Payload, SizedReader};
use crate::error::{Error, ErrorKind};
use crate::header;
use crate::header::{get_header, Header};
use crate::proxy::Proto;
use crate::resolve::ArcResolver;
use crate::response::Response;
use crate::stream::{self, connect_test, Stream};
@@ -404,12 +405,24 @@ fn send_prelude(unit: &Unit, stream: &mut Stream) -> io::Result<()> {
// build into a buffer and send in one go.
let mut prelude = PreludeBuilder::new();
let path = if let Some(proxy) = &unit.agent.config.proxy {
// HTTP proxies require the path to be in absolute URI form
// https://www.rfc-editor.org/rfc/rfc7230#section-5.3.2
match proxy.proto {
Proto::HTTP => format!(
"{}://{}{}",
unit.url.scheme(),
unit.url.host().unwrap(),
unit.url.path()
),
_ => unit.url.path().into(),
}
} else {
unit.url.path().into()
};
// request line
prelude.write_request_line(
&unit.method,
unit.url.path(),
unit.url.query().unwrap_or_default(),
)?;
prelude.write_request_line(&unit.method, &path, unit.url.query().unwrap_or_default())?;
// host header if not set by user.
if !header::has_header(&unit.headers, "host") {