| @@ -236,13 +236,14 @@ where C: Connect + Sync + 'static, | |||||||
|         match req.version() { |         match req.version() { | ||||||
|             Version::HTTP_11 => (), |             Version::HTTP_11 => (), | ||||||
|             Version::HTTP_10 => if is_http_connect { |             Version::HTTP_10 => if is_http_connect { | ||||||
|                 debug!("CONNECT is not allowed for HTTP/1.0"); |                 warn!("CONNECT is not allowed for HTTP/1.0"); | ||||||
|                 return ResponseFuture::new(Box::new(future::err(::Error::new_user_unsupported_request_method()))); |                 return ResponseFuture::new(Box::new(future::err(::Error::new_user_unsupported_request_method()))); | ||||||
|             }, |             }, | ||||||
|             other => if self.config.ver != Ver::Http2 { |             other_h2 @ Version::HTTP_2 => if self.config.ver != Ver::Http2 { | ||||||
|                 error!("Request has unsupported version \"{:?}\"", other); |                 return ResponseFuture::error_version(other_h2); | ||||||
|                 return ResponseFuture::new(Box::new(future::err(::Error::new_user_unsupported_version()))); |             }, | ||||||
|             } |             // completely unsupported HTTP version (like HTTP/0.9)! | ||||||
|  |             other => return ResponseFuture::error_version(other), | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let domain = match extract_domain(req.uri_mut(), is_http_connect) { |         let domain = match extract_domain(req.uri_mut(), is_http_connect) { | ||||||
| @@ -591,6 +592,11 @@ impl ResponseFuture { | |||||||
|             inner: fut, |             inner: fut, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fn error_version(ver: Version) -> Self { | ||||||
|  |         warn!("Request has unsupported version \"{:?}\"", ver); | ||||||
|  |         ResponseFuture::new(Box::new(future::err(::Error::new_user_unsupported_version()))) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for ResponseFuture { | impl fmt::Debug for ResponseFuture { | ||||||
|   | |||||||
| @@ -271,7 +271,7 @@ impl Http1Transaction for Server { | |||||||
|                     warn!("response with HTTP2 version coerced to HTTP/1.1"); |                     warn!("response with HTTP2 version coerced to HTTP/1.1"); | ||||||
|                     extend(dst, b"HTTP/1.1 "); |                     extend(dst, b"HTTP/1.1 "); | ||||||
|                 }, |                 }, | ||||||
|                 _ => unreachable!(), |                 other => panic!("unexpected response version: {:?}", other), | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             extend(dst, msg.head.subject.as_str().as_bytes()); |             extend(dst, msg.head.subject.as_str().as_bytes()); | ||||||
| @@ -667,7 +667,11 @@ impl Http1Transaction for Client { | |||||||
|         match msg.head.version { |         match msg.head.version { | ||||||
|             Version::HTTP_10 => extend(dst, b"HTTP/1.0"), |             Version::HTTP_10 => extend(dst, b"HTTP/1.0"), | ||||||
|             Version::HTTP_11 => extend(dst, b"HTTP/1.1"), |             Version::HTTP_11 => extend(dst, b"HTTP/1.1"), | ||||||
|             _ => unreachable!(), |             Version::HTTP_2 => { | ||||||
|  |                 warn!("request with HTTP2 version coerced to HTTP/1.1"); | ||||||
|  |                 extend(dst, b"HTTP/1.1"); | ||||||
|  |             }, | ||||||
|  |             other => panic!("unexpected request version: {:?}", other), | ||||||
|         } |         } | ||||||
|         extend(dst, b"\r\n"); |         extend(dst, b"\r\n"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,13 +39,6 @@ macro_rules! test { | |||||||
|             request: {$( |             request: {$( | ||||||
|                 $c_req_prop:ident: $c_req_val: tt, |                 $c_req_prop:ident: $c_req_val: tt, | ||||||
|             )*}, |             )*}, | ||||||
|             /* |  | ||||||
|                 method: $client_method:ident, |  | ||||||
|                 url: $client_url:expr, |  | ||||||
|                 headers: { $($request_header_name:expr => $request_header_val:expr,)* }, |  | ||||||
|                 body: $request_body:expr, |  | ||||||
|             }, |  | ||||||
|             */ |  | ||||||
|  |  | ||||||
|             response: |             response: | ||||||
|                 status: $client_status:ident, |                 status: $client_status:ident, | ||||||
| @@ -299,6 +292,10 @@ macro_rules! __client_req_prop { | |||||||
|         $req_builder.method(Method::$method); |         $req_builder.method(Method::$method); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     ($req_builder:ident, $body:ident, $addr:ident, version: $version:ident) => ({ | ||||||
|  |         $req_builder.version(hyper::Version::$version); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     ($req_builder:ident, $body:ident, $addr:ident, url: $url:expr) => ({ |     ($req_builder:ident, $body:ident, $addr:ident, url: $url:expr) => ({ | ||||||
|         $req_builder.uri(format!($url, addr=$addr)); |         $req_builder.uri(format!($url, addr=$addr)); | ||||||
|     }); |     }); | ||||||
| @@ -724,6 +721,38 @@ test! { | |||||||
|             body: None, |             body: None, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | test! { | ||||||
|  |     name: client_h1_rejects_http2, | ||||||
|  |  | ||||||
|  |     server: | ||||||
|  |         expected: "won't get here {addr}", | ||||||
|  |         reply: "won't reply", | ||||||
|  |  | ||||||
|  |     client: | ||||||
|  |         request: { | ||||||
|  |             method: GET, | ||||||
|  |             url: "http://{addr}/", | ||||||
|  |             version: HTTP_2, | ||||||
|  |         }, | ||||||
|  |         error: |err| err.to_string() == "request has unsupported HTTP version", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | test! { | ||||||
|  |     name: client_always_rejects_http09, | ||||||
|  |  | ||||||
|  |     server: | ||||||
|  |         expected: "won't get here {addr}", | ||||||
|  |         reply: "won't reply", | ||||||
|  |  | ||||||
|  |     client: | ||||||
|  |         request: { | ||||||
|  |             method: GET, | ||||||
|  |             url: "http://{addr}/", | ||||||
|  |             version: HTTP_09, | ||||||
|  |         }, | ||||||
|  |         error: |err| err.to_string() == "request has unsupported HTTP version", | ||||||
|  | } | ||||||
|  |  | ||||||
| mod dispatch_impl { | mod dispatch_impl { | ||||||
|     use super::*; |     use super::*; | ||||||
|     use std::io::{self, Read, Write}; |     use std::io::{self, Read, Write}; | ||||||
| @@ -1804,6 +1833,52 @@ mod conn { | |||||||
|         rt.block_on(res.join(rx).map(|r| r.0)).unwrap(); |         rt.block_on(res.join(rx).map(|r| r.0)).unwrap(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn http1_conn_coerces_http2_request() { | ||||||
|  |         let server = TcpListener::bind("127.0.0.1:0").unwrap(); | ||||||
|  |         let addr = server.local_addr().unwrap(); | ||||||
|  |         let mut rt = Runtime::new().unwrap(); | ||||||
|  |  | ||||||
|  |         let (tx1, rx1) = oneshot::channel(); | ||||||
|  |  | ||||||
|  |         thread::spawn(move || { | ||||||
|  |             let mut sock = server.accept().unwrap().0; | ||||||
|  |             sock.set_read_timeout(Some(Duration::from_secs(5))).unwrap(); | ||||||
|  |             sock.set_write_timeout(Some(Duration::from_secs(5))).unwrap(); | ||||||
|  |             let mut buf = [0; 4096]; | ||||||
|  |             let n = sock.read(&mut buf).expect("read 1"); | ||||||
|  |  | ||||||
|  |             // Not HTTP/2, nor panicked | ||||||
|  |             let expected = "GET /a HTTP/1.1\r\n\r\n"; | ||||||
|  |             assert_eq!(s(&buf[..n]), expected); | ||||||
|  |  | ||||||
|  |             sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").unwrap(); | ||||||
|  |             let _ = tx1.send(()); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         let tcp = rt.block_on(tcp_connect(&addr)).unwrap(); | ||||||
|  |  | ||||||
|  |         let (mut client, conn) = rt.block_on(conn::handshake(tcp)).unwrap(); | ||||||
|  |  | ||||||
|  |         rt.spawn(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e))); | ||||||
|  |  | ||||||
|  |         let req = Request::builder() | ||||||
|  |             .uri("/a") | ||||||
|  |             .version(hyper::Version::HTTP_2) | ||||||
|  |             .body(Default::default()) | ||||||
|  |             .unwrap(); | ||||||
|  |  | ||||||
|  |         let res = client.send_request(req).and_then(move |res| { | ||||||
|  |             assert_eq!(res.status(), hyper::StatusCode::OK); | ||||||
|  |             res.into_body().concat2() | ||||||
|  |         }); | ||||||
|  |         let rx = rx1.expect("thread panicked"); | ||||||
|  |  | ||||||
|  |         let timeout = Delay::new(Duration::from_millis(200)); | ||||||
|  |         let rx = rx.and_then(move |_| timeout.expect("timeout")); | ||||||
|  |         rt.block_on(res.join(rx).map(|r| r.0)).unwrap(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn pipeline() { |     fn pipeline() { | ||||||
|         let server = TcpListener::bind("127.0.0.1:0").unwrap(); |         let server = TcpListener::bind("127.0.0.1:0").unwrap(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user