diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 5e5b10c28..555c86034 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -303,6 +303,9 @@ pub const CURLPROXY_SOCKS5: curl_proxytype = 5; pub const CURLPROXY_SOCKS4A: curl_proxytype = 6; pub const CURLPROXY_SOCKS5_HOSTNAME: curl_proxytype = 7; +pub const CURLHEADER_UNIFIED: c_long = 0; +pub const CURLHEADER_SEPARATE: c_long = 1 << 0; + pub const CURLAUTH_NONE: c_ulong = 0; pub const CURLAUTH_BASIC: c_ulong = 1 << 0; pub const CURLAUTH_DIGEST: c_ulong = 1 << 1; @@ -595,6 +598,7 @@ pub const CURLOPT_SSL_OPTIONS: CURLoption = CURLOPTTYPE_LONG + 216; // pub const CURLOPT_LOGIN_OPTIONS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 224; pub const CURLOPT_EXPECT_100_TIMEOUT_MS: CURLoption = CURLOPTTYPE_LONG + 227; pub const CURLOPT_PROXYHEADER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 228; +pub const CURLOPT_HEADEROPT: CURLoption = CURLOPTTYPE_LONG + 229; pub const CURLOPT_PINNEDPUBLICKEY: CURLoption = CURLOPTTYPE_OBJECTPOINT + 230; pub const CURLOPT_UNIX_SOCKET_PATH: CURLoption = CURLOPTTYPE_OBJECTPOINT + 231; pub const CURLOPT_PATH_AS_IS: CURLoption = CURLOPTTYPE_LONG + 234; diff --git a/src/easy/handle.rs b/src/easy/handle.rs index 10bcce350..2a12a9566 100644 --- a/src/easy/handle.rs +++ b/src/easy/handle.rs @@ -851,6 +851,11 @@ impl Easy { self.inner.proxy_headers(list) } + /// Same as [`Easy2::proxy_headers_separate`](struct.Easy2.html#method.proxy_headers_separate) + pub fn proxy_headers_separate(&mut self, separate: bool) -> Result<(), Error> { + self.inner.proxy_headers_separate(separate) + } + /// Same as [`Easy2::cookie`](struct.Easy2.html#method.cookie) pub fn cookie(&mut self, cookie: &str) -> Result<(), Error> { self.inner.cookie(cookie) diff --git a/src/easy/handler.rs b/src/easy/handler.rs index d9aa7b6ff..900a90b0c 100644 --- a/src/easy/handler.rs +++ b/src/easy/handler.rs @@ -1598,7 +1598,9 @@ impl Easy2 { /// Add some headers to send to the HTTP proxy. /// - /// This function is essentially the same as `http_headers`. + /// This function is essentially the same as `http_headers`, but libcurl + /// only uses the list once [`proxy_headers_separate`](Easy2::proxy_headers_separate) + /// is on. /// /// By default this option is not set and corresponds to /// `CURLOPT_PROXYHEADER` @@ -1608,6 +1610,26 @@ impl Easy2 { self.setopt_ptr(curl_sys::CURLOPT_PROXYHEADER, ptr as *const _) } + /// Keep the headers for the proxy apart from the headers for the server. + /// + /// Off, libcurl's default, the `http_headers` list is also sent to the + /// proxy in a `CONNECT` request, which hands an `Authorization` or + /// `Cookie` header meant for the server to the proxy, and the + /// `proxy_headers` list is ignored. On, `http_headers` go only to the + /// server and `proxy_headers` only to the proxy; a request that is not + /// a `CONNECT` (plain `http://` through a proxy) gets both lists. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_HEADEROPT` with `CURLHEADER_SEPARATE`. + pub fn proxy_headers_separate(&mut self, separate: bool) -> Result<(), Error> { + let mode = if separate { + curl_sys::CURLHEADER_SEPARATE + } else { + curl_sys::CURLHEADER_UNIFIED + }; + self.setopt_long(curl_sys::CURLOPT_HEADEROPT, mode) + } + /// Set the contents of the HTTP Cookie header. /// /// Pass a string of the form `name=contents` for one cookie value or diff --git a/tests/easy.rs b/tests/easy.rs index b3fde1828..a36105870 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -1035,3 +1035,37 @@ fn test_timeout() { .unwrap() ); } + +#[test] +fn proxy_headers_need_separate_mode() { + // the server plays a plain-http proxy, which gets both lists once the + // headers are kept separate and only the server list before + for (separate, expect_proxy_header) in [(false, false), (true, true)] { + let s = Server::new(); + let mut expected = String::from( + "\ + GET http://origin.invalid/ HTTP/1.1\r\n\ + Host: origin.invalid\r\n\ + Accept: */*\r\n\ + X-Server: 1\r\n", + ); + if expect_proxy_header { + expected.push_str("X-Proxy: 1\r\n"); + } + expected.push_str("\r\n"); + s.receive(&expected); + s.send("HTTP/1.1 200 OK\r\n\r\n"); + + let mut handle = handle(); + t!(handle.url("http://origin.invalid/")); + t!(handle.proxy(&s.url(""))); + let mut server_headers = List::new(); + t!(server_headers.append("X-Server: 1")); + t!(handle.http_headers(server_headers)); + let mut proxy_headers = List::new(); + t!(proxy_headers.append("X-Proxy: 1")); + t!(handle.proxy_headers(proxy_headers)); + t!(handle.proxy_headers_separate(separate)); + t!(handle.perform()); + } +}