mod helper; use std::collections::HashMap; use h11::{ Connection, ConnectionClosed, Data, EndOfMessage, Event, EventType, Headers, ProtocolError, Request, Response, Role, }; use helper::{get_all_events, receive_and_get, ConnectionPair}; #[test] fn test_connection_basics_and_content_length() { let mut p = ConnectionPair::new(); assert_eq!( p.send( Role::Client, vec![Request::new( b"GET".to_vec(), vec![ (b"Host".to_vec(), b"example.com".to_vec()), (b"Content-Length".to_vec(), b"10".to_vec()) ] .into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(),], None, ) .unwrap(), b"GET / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 10\r\n\r\n".to_vec() ); for (_, connection) in &p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::SendBody), (Role::Server, h11::State::SendResponse), ]) ); } assert_eq!(p.conn[&Role::Client].get_our_state(), h11::State::SendBody); assert_eq!( p.conn[&Role::Client].get_their_state(), h11::State::SendResponse ); assert_eq!( p.conn[&Role::Server].get_our_state(), h11::State::SendResponse ); assert_eq!( p.conn[&Role::Server].get_their_state(), h11::State::SendBody ); assert_eq!(p.conn[&Role::Client].their_http_version, None); assert_eq!( p.conn[&Role::Server].their_http_version, Some(b"1.1".to_vec()) ); assert_eq!( p.send( Role::Server, vec![Response { status_code: 100, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into()], None ) .unwrap(), b"HTTP/1.1 100 \r\n\r\n".to_vec() ); assert_eq!( p.send( Role::Server, vec![Response { status_code: 200, headers: vec![(b"Content-Length".to_vec(), b"11".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into()], None ) .unwrap(), b"HTTP/1.1 200 \r\nContent-Length: 11\r\n\r\n".to_vec() ); for (_, connection) in &p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::SendBody), (Role::Server, h11::State::SendBody), ]) ); } assert_eq!( p.conn[&Role::Client].their_http_version, Some(b"1.1".to_vec()) ); assert_eq!( p.conn[&Role::Server].their_http_version, Some(b"1.1".to_vec()) ); assert_eq!( p.send( Role::Client, vec![Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, } .into()], None ) .unwrap(), b"12345".to_vec() ); assert_eq!( p.send( Role::Client, vec![Data { data: b"67890".to_vec(), chunk_start: false, chunk_end: false, } .into()], Some(vec![ Data { data: b"67890".to_vec(), chunk_start: false, chunk_end: false, } .into(), EndOfMessage::default().into(), ]), ) .unwrap(), b"67890".to_vec() ); assert_eq!( p.send( Role::Client, vec![EndOfMessage::default().into()], Some(vec![]), ) .unwrap(), b"".to_vec() ); for (_, connection) in &p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::Done), (Role::Server, h11::State::SendBody), ]) ); } assert_eq!( p.send( Role::Server, vec![Data { data: b"1234567890".to_vec(), chunk_start: false, chunk_end: false, } .into()], None ) .unwrap(), b"1234567890".to_vec() ); assert_eq!( p.send( Role::Server, vec![Data { data: b"1".to_vec(), chunk_start: false, chunk_end: false, } .into()], Some(vec![ Data { data: b"1".to_vec(), chunk_start: false, chunk_end: false, } .into(), EndOfMessage::default().into(), ]), ) .unwrap(), b"1".to_vec() ); assert_eq!( p.send( Role::Server, vec![EndOfMessage::default().into()], Some(vec![]), ) .unwrap(), b"".to_vec() ); for (_, connection) in &p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::Done), (Role::Server, h11::State::Done), ]) ); } } #[test] fn test_chunked() { let mut p = ConnectionPair::new(); assert_eq!( p.send( Role::Client, vec![Request::new( b"GET".to_vec(), vec![ (b"Host".to_vec(), b"example.com".to_vec()), (b"Transfer-Encoding".to_vec(), b"chunked".to_vec()) ] .into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(),], None, ) .unwrap(), b"GET / HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n".to_vec() ); assert_eq!( p.send( Role::Client, vec![Data { data: b"1234567890".to_vec(), chunk_start: true, chunk_end: true, } .into()], None, ) .unwrap(), b"a\r\n1234567890\r\n".to_vec() ); assert_eq!( p.send( Role::Client, vec![Data { data: b"abcde".to_vec(), chunk_start: true, chunk_end: true, } .into()], None, ) .unwrap(), b"5\r\nabcde\r\n".to_vec() ); assert_eq!( p.send(Role::Client, vec![Data::default().into()], Some(vec![]),) .unwrap(), b"".to_vec() ); assert_eq!( p.send( Role::Client, vec![EndOfMessage { headers: vec![(b"hello".to_vec(), b"there".to_vec())].into(), } .into()], None, ) .unwrap(), b"0\r\nhello: there\r\n\r\n".to_vec() ); assert_eq!( p.send( Role::Server, vec![Response { status_code: 200, headers: vec![ (b"hello".to_vec(), b"there".to_vec()), (b"transfer-encoding".to_vec(), b"chunked".to_vec()), ] .into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into()], None, ) .unwrap(), b"HTTP/1.1 200 \r\nhello: there\r\ntransfer-encoding: chunked\r\n\r\n".to_vec() ); assert_eq!( p.send( Role::Server, vec![Data { data: b"54321".to_vec(), chunk_start: true, chunk_end: true, } .into()], None, ) .unwrap(), b"5\r\n54321\r\n".to_vec() ); assert_eq!( p.send( Role::Server, vec![Data { data: b"12345".to_vec(), chunk_start: true, chunk_end: true, } .into()], None, ) .unwrap(), b"5\r\n12345\r\n".to_vec() ); assert_eq!( p.send(Role::Server, vec![EndOfMessage::default().into()], None,) .unwrap(), b"0\r\n\r\n".to_vec() ); for (_, connection) in &p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::Done), (Role::Server, h11::State::Done), ]) ); } } #[test] fn test_chunk_boundaries() { let mut conn = Connection::new(Role::Server, None); let request = b"POST / HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n"; conn.receive_data(request).unwrap(); assert_eq!( conn.next_event().unwrap(), Event::Request(Request { method: b"POST".to_vec(), target: b"/".to_vec(), headers: vec![ (b"Host".to_vec(), b"example.com".to_vec()), (b"Transfer-Encoding".to_vec(), b"chunked".to_vec()) ] .into(), http_version: b"1.1".to_vec(), }) ); assert_eq!(conn.next_event().unwrap(), Event::NeedData {}); conn.receive_data(b"5\r\nhello\r\n").unwrap(); assert_eq!( conn.next_event().unwrap(), Event::Data(Data { data: b"hello".to_vec(), chunk_start: true, chunk_end: true, }) ); conn.receive_data(b"5\r\nhel").unwrap(); assert_eq!( conn.next_event().unwrap(), Event::Data(Data { data: b"hel".to_vec(), chunk_start: true, chunk_end: false, }) ); conn.receive_data(b"l").unwrap(); assert_eq!( conn.next_event().unwrap(), Event::Data(Data { data: b"l".to_vec(), chunk_start: false, chunk_end: false, }) ); conn.receive_data(b"o\r\n").unwrap(); assert_eq!( conn.next_event().unwrap(), Event::Data(Data { data: b"o".to_vec(), chunk_start: false, chunk_end: true, }) ); conn.receive_data(b"5\r\nhello").unwrap(); assert_eq!( conn.next_event().unwrap(), Event::Data(Data { data: b"hello".to_vec(), chunk_start: true, chunk_end: true, }) ); conn.receive_data(b"\r\n").unwrap(); assert_eq!(conn.next_event().unwrap(), Event::NeedData {}); conn.receive_data(b"0\r\n\r\n").unwrap(); assert_eq!( conn.next_event().unwrap(), Event::EndOfMessage(EndOfMessage { headers: vec![].into(), }) ); } // def test_client_talking_to_http10_server() -> None: // c = Connection(CLIENT) // c.send(Request(method="GET", target="/", headers=[("Host", "example.com")])) // c.send(EndOfMessage()) // assert c.our_state is DONE // # No content-length, so Http10 framing for body // assert receive_and_get(c, b"HTTP/1.0 200 OK\r\n\r\n") == [ // Response(status_code=200, headers=[], http_version="1.0", reason=b"OK") # type: ignore[arg-type] // ] // assert c.our_state is MUST_CLOSE // assert receive_and_get(c, b"12345") == [Data(data=b"12345")] // assert receive_and_get(c, b"67890") == [Data(data=b"67890")] // assert receive_and_get(c, b"") == [EndOfMessage(), ConnectionClosed()] // assert c.their_state is CLOSED #[test] fn test_client_talking_to_http10_server() { let mut c = Connection::new(Role::Client, None); c.send( Request::new( b"GET".to_vec(), vec![(b"Host".to_vec(), b"example.com".to_vec())].into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); assert_eq!(c.get_our_state(), h11::State::Done); assert_eq!( receive_and_get(&mut c, b"HTTP/1.0 200 OK\r\n\r\n").unwrap(), vec![Event::NormalResponse(Response { status_code: 200, headers: vec![].into(), http_version: b"1.0".to_vec(), reason: b"OK".to_vec(), })], ); assert_eq!(c.get_our_state(), h11::State::MustClose); assert_eq!( receive_and_get(&mut c, b"12345").unwrap(), vec![Event::Data(Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, })], ); assert_eq!( receive_and_get(&mut c, b"67890").unwrap(), vec![Event::Data(Data { data: b"67890".to_vec(), chunk_start: false, chunk_end: false, })], ); assert_eq!( receive_and_get(&mut c, b"").unwrap(), vec![ Event::EndOfMessage(EndOfMessage::default()), Event::ConnectionClosed(ConnectionClosed::default()), ], ); assert_eq!(c.get_their_state(), h11::State::Closed); } // def test_server_talking_to_http10_client() -> None: // c = Connection(SERVER) // # No content-length, so no body // # NB: no host header // assert receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") == [ // Request(method="GET", target="/", headers=[], http_version="1.0"), # type: ignore[arg-type] // EndOfMessage(), // ] // assert c.their_state is MUST_CLOSE // # We automatically Connection: close back at them // assert ( // c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] // == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n" // ) // assert c.send(Data(data=b"12345")) == b"12345" // assert c.send(EndOfMessage()) == b"" // assert c.our_state is MUST_CLOSE // # Check that it works if they do send Content-Length // c = Connection(SERVER) // # NB: no host header // assert receive_and_get(c, b"POST / HTTP/1.0\r\nContent-Length: 10\r\n\r\n1") == [ // Request( // method="POST", // target="/", // headers=[("Content-Length", "10")], // http_version="1.0", // ), // Data(data=b"1"), // ] // assert receive_and_get(c, b"234567890") == [Data(data=b"234567890"), EndOfMessage()] // assert c.their_state is MUST_CLOSE // assert receive_and_get(c, b"") == [ConnectionClosed()] #[test] fn test_server_talking_to_http10_client() { let mut c = Connection::new(Role::Server, None); // No content-length, so no body // NB: no host header assert_eq!( receive_and_get(&mut c, b"GET / HTTP/1.0\r\n\r\n").unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/".to_vec(), headers: vec![].into(), http_version: b"1.0".to_vec(), }), Event::EndOfMessage(EndOfMessage::default()), ], ); assert_eq!(c.get_their_state(), h11::State::MustClose); // We automatically Connection: close back at them assert_eq!( c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into() ) .unwrap() .unwrap(), b"HTTP/1.1 200 \r\nconnection: close\r\n\r\n".to_vec() ); assert_eq!( c.send( Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, } .into() ) .unwrap() .unwrap(), b"12345".to_vec() ); assert_eq!( c.send(EndOfMessage::default().into()).unwrap().unwrap(), b"".to_vec() ); assert_eq!(c.get_our_state(), h11::State::MustClose); // Check that it works if they do send Content-Length let mut c = Connection::new(Role::Server, None); // NB: no host header assert_eq!( receive_and_get(&mut c, b"POST / HTTP/1.0\r\nContent-Length: 10\r\n\r\n1").unwrap(), vec![ Event::Request(Request { method: b"POST".to_vec(), target: b"/".to_vec(), headers: vec![(b"Content-Length".to_vec(), b"10".to_vec())].into(), http_version: b"1.0".to_vec(), }), Event::Data(Data { data: b"1".to_vec(), chunk_start: false, chunk_end: false, }), ], ); assert_eq!( receive_and_get(&mut c, b"234567890").unwrap(), vec![ Event::Data(Data { data: b"234567890".to_vec(), chunk_start: false, chunk_end: false, }), Event::EndOfMessage(EndOfMessage::default()), ], ); assert_eq!(c.get_their_state(), h11::State::MustClose); assert_eq!( receive_and_get(&mut c, b"").unwrap(), vec![Event::ConnectionClosed(ConnectionClosed::default())], ); } // def test_automatic_transfer_encoding_in_response() -> None: // # Check that in responses, the user can specify either Transfer-Encoding: // # chunked or no framing at all, and in both cases we automatically select // # the right option depending on whether the peer speaks HTTP/1.0 or // # HTTP/1.1 // for user_headers in [ // [("Transfer-Encoding", "chunked")], // [], // # In fact, this even works if Content-Length is set, // # because if both are set then Transfer-Encoding wins // [("Transfer-Encoding", "chunked"), ("Content-Length", "100")], // ]: // user_headers = cast(List[Tuple[str, str]], user_headers) // p = ConnectionPair() // p.send( // CLIENT, // [ // Request(method="GET", target="/", headers=[("Host", "example.com")]), // EndOfMessage(), // ], // ) // # When speaking to HTTP/1.1 client, all of the above cases get // # normalized to Transfer-Encoding: chunked // p.send( // SERVER, // Response(status_code=200, headers=user_headers), // expect=Response( // status_code=200, headers=[("Transfer-Encoding", "chunked")] // ), // ) // # When speaking to HTTP/1.0 client, all of the above cases get // # normalized to no-framing-headers // c = Connection(SERVER) // receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") // assert ( // c.send(Response(status_code=200, headers=user_headers)) // == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n" // ) // assert c.send(Data(data=b"12345")) == b"12345" #[test] fn test_automatic_transfer_encoding_in_response() { // Check that in responses, the user can specify either Transfer-Encoding: // chunked or no framing at all, and in both cases we automatically select // the right option depending on whether the peer speaks HTTP/1.0 or // HTTP/1.1 for user_headers in vec![ vec![(b"Transfer-Encoding".to_vec(), b"chunked".to_vec())], vec![], // In fact, this even works if Content-Length is set, // because if both are set then Transfer-Encoding wins vec![ (b"Transfer-Encoding".to_vec(), b"chunked".to_vec()), (b"Content-Length".to_vec(), b"100".to_vec()), ], ] { let mut p = ConnectionPair::new(); p.send( Role::Client, vec![ Request::new( b"GET".to_vec(), vec![(b"Host".to_vec(), b"example.com".to_vec())].into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), EndOfMessage::default().into(), ], None, ) .unwrap(); // When speaking to HTTP/1.1 client, all of the above cases get // normalized to Transfer-Encoding: chunked p.send( Role::Server, vec![Response { status_code: 200, headers: user_headers.clone().into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into()], Some(vec![Response { status_code: 200, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into()]), ) .unwrap(); // When speaking to HTTP/1.0 client, all of the above cases get // normalized to no-framing-headers let mut c = Connection::new(Role::Server, None); receive_and_get(&mut c, b"GET / HTTP/1.0\r\n\r\n").unwrap(); assert_eq!( c.send( Response { status_code: 200, headers: user_headers.clone().into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into() ) .unwrap() .unwrap(), b"HTTP/1.1 200 \r\nconnection: close\r\n\r\n".to_vec() ); assert_eq!( c.send( Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, } .into() ) .unwrap() .unwrap(), b"12345".to_vec() ); } } // def test_automagic_connection_close_handling() -> None: // p = ConnectionPair() // # If the user explicitly sets Connection: close, then we notice and // # respect it // p.send( // CLIENT, // [ // Request( // method="GET", // target="/", // headers=[("Host", "example.com"), ("Connection", "close")], // ), // EndOfMessage(), // ], // ) // for conn in p.conns: // assert conn.states[CLIENT] is MUST_CLOSE // # And if the client sets it, the server automatically echoes it back // p.send( // SERVER, // # no header here... // [Response(status_code=204, headers=[]), EndOfMessage()], # type: ignore[arg-type] // # ...but oh look, it arrived anyway // expect=[ // Response(status_code=204, headers=[("connection", "close")]), // EndOfMessage(), // ], // ) // for conn in p.conns: // assert conn.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE} #[test] fn test_automagic_connection_close_handling() { let mut p = ConnectionPair::new(); // If the user explicitly sets Connection: close, then we notice and // respect it p.send( Role::Client, vec![ Request::new( b"GET".to_vec(), vec![ (b"Host".to_vec(), b"example.com".to_vec()), (b"Connection".to_vec(), b"close".to_vec()), ] .into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), EndOfMessage::default().into(), ], None, ) .unwrap(); for (_, connection) in &p.conn { assert_eq!( connection.get_states()[&Role::Client], h11::State::MustClose ); } // And if the client sets it, the server automatically echoes it back p.send( Role::Server, vec![ Response { status_code: 204, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), EndOfMessage::default().into(), ], Some(vec![ Response { status_code: 204, headers: vec![(b"connection".to_vec(), b"close".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), EndOfMessage::default().into(), ]), ) .unwrap(); for (_, connection) in &p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::MustClose), (Role::Server, h11::State::MustClose), ]) ); } } // def test_100_continue() -> None: // def setup() -> ConnectionPair: // p = ConnectionPair() // p.send( // CLIENT, // Request( // method="GET", // target="/", // headers=[ // ("Host", "example.com"), // ("Content-Length", "100"), // ("Expect", "100-continue"), // ], // ), // ) // for conn in p.conns: // assert conn.get_client_is_waiting_for_100_continue() // assert not p.conn[CLIENT].they_are_waiting_for_100_continue // assert p.conn[SERVER].they_are_waiting_for_100_continue // return p // # Disabled by 100 Continue // p = setup() // p.send(SERVER, InformationalResponse(status_code=100, headers=[])) # type: ignore[arg-type] // for conn in p.conns: // assert not conn.get_client_is_waiting_for_100_continue() // assert not conn.they_are_waiting_for_100_continue // # Disabled by a real response // p = setup() // p.send( // SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")]) // ) // for conn in p.conns: // assert not conn.get_client_is_waiting_for_100_continue() // assert not conn.they_are_waiting_for_100_continue // # Disabled by the client going ahead and sending stuff anyway // p = setup() // p.send(CLIENT, Data(data=b"12345")) // for conn in p.conns: // assert not conn.get_client_is_waiting_for_100_continue() // assert not conn.they_are_waiting_for_100_continue #[test] fn test_100_continue() { fn setup() -> ConnectionPair { let mut p = ConnectionPair::new(); p.send( Role::Client, vec![Request::new( b"GET".to_vec(), vec![ (b"Host".to_vec(), b"example.com".to_vec()), (b"Content-Length".to_vec(), b"100".to_vec()), (b"Expect".to_vec(), b"100-continue".to_vec()), ] .into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into()], None, ) .unwrap(); for (_, connection) in &p.conn { assert!(connection.get_client_is_waiting_for_100_continue()); } assert!(!p.conn[&Role::Client].get_they_are_waiting_for_100_continue()); assert!(p.conn[&Role::Server].get_they_are_waiting_for_100_continue()); p } // Disabled by 100 Continue let mut p = setup(); p.send( Role::Server, vec![Response { status_code: 100, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into()], None, ) .unwrap(); for (_, connection) in &p.conn { assert!(!connection.get_client_is_waiting_for_100_continue()); assert!(!connection.get_they_are_waiting_for_100_continue()); } // Disabled by a real response let mut p = setup(); p.send( Role::Server, vec![Response { status_code: 200, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into()], None, ) .unwrap(); for (_, connection) in &p.conn { assert!(!connection.get_client_is_waiting_for_100_continue()); assert!(!connection.get_they_are_waiting_for_100_continue()); } // Disabled by the client going ahead and sending stuff anyway let mut p = setup(); p.send( Role::Client, vec![Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, } .into()], None, ) .unwrap(); for (_, connection) in &p.conn { assert!(!connection.get_client_is_waiting_for_100_continue()); assert!(!connection.get_they_are_waiting_for_100_continue()); } // Disabled by the client going ahead and sending stuff anyway let mut p = setup(); p.send( Role::Client, vec![Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, } .into()], None, ) .unwrap(); for (_, connection) in &p.conn { assert!(!connection.get_client_is_waiting_for_100_continue()); assert!(!connection.get_they_are_waiting_for_100_continue()); } // Disabled by the client going ahead and sending stuff anyway let mut p = setup(); p.send( Role::Client, vec![Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, } .into()], None, ) .unwrap(); for (_, connection) in &p.conn { assert!(!connection.get_client_is_waiting_for_100_continue()); assert!(!connection.get_they_are_waiting_for_100_continue()); } } // def test_max_incomplete_event_size_countermeasure() -> None: // # Infinitely long headers are definitely not okay // c = Connection(SERVER) // c.receive_data(b"GET / HTTP/1.0\r\nEndless: ") // assert c.next_event() is NEED_DATA // with pytest.raises(RemoteProtocolError): // while True: // c.receive_data(b"a" * 1024) // c.next_event() // # Checking that the same header is accepted / rejected depending on the // # max_incomplete_event_size setting: // c = Connection(SERVER, max_incomplete_event_size=5000) // c.receive_data(b"GET / HTTP/1.0\r\nBig: ") // c.receive_data(b"a" * 4000) // c.receive_data(b"\r\n\r\n") // assert get_all_events(c) == [ // Request( // method="GET", target="/", http_version="1.0", headers=[("big", "a" * 4000)] // ), // EndOfMessage(), // ] // c = Connection(SERVER, max_incomplete_event_size=4000) // c.receive_data(b"GET / HTTP/1.0\r\nBig: ") // c.receive_data(b"a" * 4000) // with pytest.raises(RemoteProtocolError): // c.next_event() // # Temporarily exceeding the size limit is fine, as long as its done with // # complete events: // c = Connection(SERVER, max_incomplete_event_size=5000) // c.receive_data(b"GET / HTTP/1.0\r\nContent-Length: 10000") // c.receive_data(b"\r\n\r\n" + b"a" * 10000) // assert get_all_events(c) == [ // Request( // method="GET", // target="/", // http_version="1.0", // headers=[("Content-Length", "10000")], // ), // Data(data=b"a" * 10000), // EndOfMessage(), // ] // c = Connection(SERVER, max_incomplete_event_size=100) // # Two pipelined requests to create a way-too-big receive buffer... but // # it's fine because we're not checking // c.receive_data( // b"GET /1 HTTP/1.1\r\nHost: a\r\n\r\n" // b"GET /2 HTTP/1.1\r\nHost: b\r\n\r\n" + b"X" * 1000 // ) // assert get_all_events(c) == [ // Request(method="GET", target="/1", headers=[("host", "a")]), // EndOfMessage(), // ] // # Even more data comes in, still no problem // c.receive_data(b"X" * 1000) // # We can respond and reuse to get the second pipelined request // c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] // c.send(EndOfMessage()) // c.start_next_cycle() // assert get_all_events(c) == [ // Request(method="GET", target="/2", headers=[("host", "b")]), // EndOfMessage(), // ] // # But once we unpause and try to read the next message, and find that it's // # incomplete and the buffer is *still* way too large, then *that's* a // # problem: // c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] // c.send(EndOfMessage()) // c.start_next_cycle() // with pytest.raises(RemoteProtocolError): // c.next_event() #[test] fn test_max_incomplete_event_size_countermeasure() { // Infinitely long headers are definitely not okay let mut c = Connection::new(Role::Server, Some(5000)); c.receive_data(b"GET / HTTP/1.0\r\nEndless: ").unwrap(); assert_eq!(c.next_event().unwrap(), Event::NeedData {}); // Checking that the same header is accepted / rejected depending on the // max_incomplete_event_size setting: let mut c = Connection::new(Role::Server, Some(5000)); c.receive_data(b"GET / HTTP/1.0\r\nBig: ").unwrap(); c.receive_data(&vec![b'a'; 4000]).unwrap(); c.receive_data(b"\r\n\r\n").unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/".to_vec(), headers: vec![(b"Big".to_vec(), vec![b'a'; 4000])].into(), http_version: b"1.0".to_vec(), }), Event::EndOfMessage(EndOfMessage::default()), ], ); let mut c = Connection::new(Role::Server, Some(4000)); c.receive_data(b"GET / HTTP/1.0\r\nBig: ").unwrap(); c.receive_data(&vec![b'a'; 4000]).unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); // Temporarily exceeding the size limit is fine, as long as its done with // complete events: let mut c = Connection::new(Role::Server, Some(5000)); c.receive_data(b"GET / HTTP/1.0\r\nContent-Length: 10000") .unwrap(); c.receive_data(b"\r\n\r\n").unwrap(); c.receive_data(&vec![b'a'; 10000]).unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/".to_vec(), headers: vec![(b"Content-Length".to_vec(), b"10000".to_vec())].into(), http_version: b"1.0".to_vec(), }), Event::Data(Data { data: vec![b'a'; 10000], chunk_start: false, chunk_end: false, }), Event::EndOfMessage(EndOfMessage::default()), ], ); let mut c = Connection::new(Role::Server, Some(100)); // Two pipelined requests to create a way-too-big receive buffer... but // it's fine because we're not checking c.receive_data( b"GET /1 HTTP/1.1\r\nHost: a\r\n\r\n" .to_vec() .into_iter() .chain(b"GET /2 HTTP/1.1\r\nHost: b\r\n\r\n".to_vec().into_iter()) .chain(vec![b'X'; 1000].into_iter()) .collect::>() .as_slice(), ) .unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/1".to_vec(), headers: vec![(b"Host".to_vec(), b"a".to_vec())].into(), http_version: b"1.1".to_vec(), }), Event::EndOfMessage(EndOfMessage::default()), ], ); // Even more data comes in, still no problem c.receive_data(&vec![b'X'; 1000]).unwrap(); // We can respond and reuse to get the second pipelined request c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); c.start_next_cycle().unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/2".to_vec(), headers: vec![(b"Host".to_vec(), b"b".to_vec())].into(), http_version: b"1.1".to_vec(), }), Event::EndOfMessage(EndOfMessage::default()), ], ); // But once we unpause and try to read the next message, and find that it's // incomplete and the buffer is *still* way too large, then *that's* a // problem: c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); c.start_next_cycle().unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); // Check that we can still send data after this happens let mut c = Connection::new(Role::Server, Some(100)); // Two pipelined requests to create a way-too-big receive buffer... but // it's fine because we're not checking c.receive_data( b"GET /1 HTTP/1.1\r\nHost: a\r\n\r\n" .to_vec() .into_iter() .chain(b"GET /2 HTTP/1.1\r\nHost: b\r\n\r\n".to_vec().into_iter()) .chain(vec![b'X'; 1000].into_iter()) .collect::>() .as_slice(), ) .unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/1".to_vec(), headers: vec![(b"Host".to_vec(), b"a".to_vec())].into(), http_version: b"1.1".to_vec(), }), Event::EndOfMessage(EndOfMessage::default()), ], ); // Even more data comes in, still no problem c.receive_data(&vec![b'X'; 1000]).unwrap(); // We can respond and reuse to get the second pipelined request c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); c.start_next_cycle().unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/2".to_vec(), headers: vec![(b"Host".to_vec(), b"b".to_vec())].into(), http_version: b"1.1".to_vec(), }), Event::EndOfMessage(EndOfMessage::default()), ], ); // But once we unpause and try to read the next message, and find that it's // incomplete and the buffer is *still* way too large, then *that's* a // problem: c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); c.start_next_cycle().unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); } // def test_reuse_simple() -> None: // p = ConnectionPair() // p.send( // CLIENT, // [Request(method="GET", target="/", headers=[("Host", "a")]), EndOfMessage()], // ) // p.send( // SERVER, // [ // Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), // EndOfMessage(), // ], // ) // for conn in p.conns: // assert conn.states == {CLIENT: DONE, SERVER: DONE} // conn.start_next_cycle() // p.send( // CLIENT, // [ // Request(method="DELETE", target="/foo", headers=[("Host", "a")]), // EndOfMessage(), // ], // ) // p.send( // SERVER, // [ // Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), // EndOfMessage(), // ], // ) #[test] fn test_reuse_simple() { let mut p = ConnectionPair::new(); p.send( Role::Client, vec![ Request::new( b"GET".to_vec(), vec![(b"Host".to_vec(), b"a".to_vec())].into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), EndOfMessage::default().into(), ], None, ) .unwrap(); p.send( Role::Server, vec![ Response { status_code: 200, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), EndOfMessage::default().into(), ], None, ) .unwrap(); for (_, connection) in &mut p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::Done), (Role::Server, h11::State::Done), ]) ); connection.start_next_cycle().unwrap(); } p.send( Role::Client, vec![ Request::new( b"DELETE".to_vec(), vec![(b"Host".to_vec(), b"a".to_vec())].into(), b"/foo".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), EndOfMessage::default().into(), ], None, ) .unwrap(); p.send( Role::Server, vec![ Response { status_code: 404, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), EndOfMessage::default().into(), ], None, ) .unwrap(); } // def test_pipelining() -> None: // # Client doesn't support pipelining, so we have to do this by hand // c = Connection(SERVER) // assert c.next_event() is NEED_DATA // # 3 requests all bunched up // c.receive_data( // b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" // b"12345" // b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" // b"67890" // b"GET /3 HTTP/1.1\r\nHost: a.com\r\n\r\n" // ) // assert get_all_events(c) == [ // Request( // method="GET", // target="/1", // headers=[("Host", "a.com"), ("Content-Length", "5")], // ), // Data(data=b"12345"), // EndOfMessage(), // ] // assert c.their_state is DONE // assert c.our_state is SEND_RESPONSE // assert c.next_event() is PAUSED // c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] // c.send(EndOfMessage()) // assert c.their_state is DONE // assert c.our_state is DONE // c.start_next_cycle() // assert get_all_events(c) == [ // Request( // method="GET", // target="/2", // headers=[("Host", "a.com"), ("Content-Length", "5")], // ), // Data(data=b"67890"), // EndOfMessage(), // ] // assert c.next_event() is PAUSED // c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] // c.send(EndOfMessage()) // c.start_next_cycle() // assert get_all_events(c) == [ // Request(method="GET", target="/3", headers=[("Host", "a.com")]), // EndOfMessage(), // ] // # Doesn't pause this time, no trailing data // assert c.next_event() is NEED_DATA // c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] // c.send(EndOfMessage()) // # Arrival of more data triggers pause // assert c.next_event() is NEED_DATA // c.receive_data(b"SADF") // assert c.next_event() is PAUSED // assert c.trailing_data == (b"SADF", False) // # If EOF arrives while paused, we don't see that either: // c.receive_data(b"") // assert c.trailing_data == (b"SADF", True) // assert c.next_event() is PAUSED // c.receive_data(b"") // assert c.next_event() is PAUSED #[test] fn test_pipelining() { // Client doesn't support pipelining, so we have to do this by hand let mut c = Connection::new(Role::Server, None); assert_eq!(c.next_event().unwrap(), Event::NeedData {}); // 3 requests all bunched up c.receive_data( &vec![ b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n".to_vec(), b"12345".to_vec(), b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n".to_vec(), b"67890".to_vec(), b"GET /3 HTTP/1.1\r\nHost: a.com\r\n\r\n".to_vec(), ] .into_iter() .flatten() .collect::>(), ) .unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/1".to_vec(), headers: vec![ (b"Host".to_vec(), b"a.com".to_vec()), (b"Content-Length".to_vec(), b"5".to_vec()) ] .into(), http_version: b"1.1".to_vec(), }), Event::Data(Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, }), Event::EndOfMessage(EndOfMessage::default()), ], ); assert_eq!(c.get_their_state(), h11::State::Done); assert_eq!(c.get_our_state(), h11::State::SendResponse); assert_eq!(c.next_event().unwrap(), Event::Paused {}); c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); assert_eq!(c.get_their_state(), h11::State::Done); assert_eq!(c.get_our_state(), h11::State::Done); c.start_next_cycle().unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/2".to_vec(), headers: vec![ (b"Host".to_vec(), b"a.com".to_vec()), (b"Content-Length".to_vec(), b"5".to_vec()) ] .into(), http_version: b"1.1".to_vec(), }), Event::Data(Data { data: b"67890".to_vec(), chunk_start: false, chunk_end: false, }), Event::EndOfMessage(EndOfMessage::default()), ], ); assert_eq!(c.next_event().unwrap(), Event::Paused {}); c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); c.start_next_cycle().unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/3".to_vec(), headers: vec![(b"Host".to_vec(), b"a.com".to_vec())].into(), http_version: b"1.1".to_vec(), }), Event::EndOfMessage(EndOfMessage::default()), ], ); // Doesn't pause this time, no trailing data assert_eq!(c.next_event().unwrap(), Event::NeedData {}); c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); // Arrival of more data triggers pause assert_eq!(c.next_event().unwrap(), Event::NeedData {}); c.receive_data(b"SADF").unwrap(); assert_eq!(c.next_event().unwrap(), Event::Paused {}); assert_eq!(c.get_trailing_data(), (b"SADF".to_vec(), false)); // If EOF arrives while paused, we don't see that either: c.receive_data(b"").unwrap(); assert_eq!(c.get_trailing_data(), (b"SADF".to_vec(), true)); assert_eq!(c.next_event().unwrap(), Event::Paused {}); c.receive_data(b"").unwrap(); assert_eq!(c.next_event().unwrap(), Event::Paused {}); } // def test_protocol_switch() -> None: // for req, deny, accept in [ // ( // Request( // method="CONNECT", // target="example.com:443", // headers=[("Host", "foo"), ("Content-Length", "1")], // ), // Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), // Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), // ), // ( // Request( // method="GET", // target="/", // headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], // ), // Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), // InformationalResponse(status_code=101, headers=[("Upgrade", "a")]), // ), // ( // Request( // method="CONNECT", // target="example.com:443", // headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], // ), // Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), // # Accept CONNECT, not upgrade // Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), // ), // ( // Request( // method="CONNECT", // target="example.com:443", // headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], // ), // Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), // # Accept Upgrade, not CONNECT // InformationalResponse(status_code=101, headers=[("Upgrade", "b")]), // ), // ]: // def setup() -> ConnectionPair: // p = ConnectionPair() // p.send(CLIENT, req) // # No switch-related state change stuff yet; the client has to // # finish the request before that kicks in // for conn in p.conns: // assert conn.states[CLIENT] is SEND_BODY // p.send(CLIENT, [Data(data=b"1"), EndOfMessage()]) // for conn in p.conns: // assert conn.states[CLIENT] is MIGHT_SWITCH_PROTOCOL // assert p.conn[SERVER].next_event() is PAUSED // return p // # Test deny case // p = setup() // p.send(SERVER, deny) // for conn in p.conns: // assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY} // p.send(SERVER, EndOfMessage()) // # Check that re-use is still allowed after a denial // for conn in p.conns: // conn.start_next_cycle() // # Test accept case // p = setup() // p.send(SERVER, accept) // for conn in p.conns: // assert conn.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} // conn.receive_data(b"123") // assert conn.next_event() is PAUSED // conn.receive_data(b"456") // assert conn.next_event() is PAUSED // assert conn.trailing_data == (b"123456", False) // # Pausing in might-switch, then recovery // # (weird artificial case where the trailing data actually is valid // # HTTP for some reason, because this makes it easier to test the state // # logic) // p = setup() // sc = p.conn[SERVER] // sc.receive_data(b"GET / HTTP/1.0\r\n\r\n") // assert sc.next_event() is PAUSED // assert sc.trailing_data == (b"GET / HTTP/1.0\r\n\r\n", False) // sc.send(deny) // assert sc.next_event() is PAUSED // sc.send(EndOfMessage()) // sc.start_next_cycle() // assert get_all_events(sc) == [ // Request(method="GET", target="/", headers=[], http_version="1.0"), # type: ignore[arg-type] // EndOfMessage(), // ] // # When we're DONE, have no trailing data, and the connection gets // # closed, we report ConnectionClosed(). When we're in might-switch or // # switched, we don't. // p = setup() // sc = p.conn[SERVER] // sc.receive_data(b"") // assert sc.next_event() is PAUSED // assert sc.trailing_data == (b"", True) // p.send(SERVER, accept) // assert sc.next_event() is PAUSED // p = setup() // sc = p.conn[SERVER] // sc.receive_data(b"") // assert sc.next_event() is PAUSED // sc.send(deny) // assert sc.next_event() == ConnectionClosed() // # You can't send after switching protocols, or while waiting for a // # protocol switch // p = setup() // with pytest.raises(LocalProtocolError): // p.conn[CLIENT].send( // Request(method="GET", target="/", headers=[("Host", "a")]) // ) // p = setup() // p.send(SERVER, accept) // with pytest.raises(LocalProtocolError): // p.conn[SERVER].send(Data(data=b"123")) #[test] fn test_protocol_switch() { for (req, deny, accept) in vec![ ( Request::new( b"CONNECT".to_vec(), vec![ (b"Host".to_vec(), b"foo".to_vec()), (b"Content-Length".to_vec(), b"1".to_vec()), ] .into(), b"example.com:443".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), Response { status_code: 404, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), Response { status_code: 200, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ), ( Request::new( b"GET".to_vec(), vec![ (b"Host".to_vec(), b"foo".to_vec()), (b"Content-Length".to_vec(), b"1".to_vec()), (b"Upgrade".to_vec(), b"a, b".to_vec()), ] .into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), Response { status_code: 200, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), Response { status_code: 101, headers: vec![(b"Upgrade".to_vec(), b"a".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ), ( Request::new( b"CONNECT".to_vec(), vec![ (b"Host".to_vec(), b"foo".to_vec()), (b"Content-Length".to_vec(), b"1".to_vec()), (b"Upgrade".to_vec(), b"a, b".to_vec()), ] .into(), b"example.com:443".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), Response { status_code: 404, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), // Accept CONNECT, not upgrade Response { status_code: 200, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ), ( Request::new( b"CONNECT".to_vec(), vec![ (b"Host".to_vec(), b"foo".to_vec()), (b"Content-Length".to_vec(), b"1".to_vec()), (b"Upgrade".to_vec(), b"a, b".to_vec()), ] .into(), b"example.com:443".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), Response { status_code: 404, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), // Accept Upgrade, not CONNECT Response { status_code: 101, headers: vec![(b"Upgrade".to_vec(), b"b".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ), ] { let req: Event = req; let deny: Event = deny; let accept: Event = accept; let setup = || { let mut p = ConnectionPair::new(); p.send(Role::Client, vec![req.clone()], None).unwrap(); // No switch-related state change stuff yet; the client has to // finish the request before that kicks in for (_, connection) in &mut p.conn { assert_eq!(connection.get_states()[&Role::Client], h11::State::SendBody); } p.send( Role::Client, vec![ Data { data: b"1".to_vec(), chunk_start: false, chunk_end: false, } .into(), EndOfMessage::default().into(), ], None, ) .unwrap(); for (_, connection) in &mut p.conn { assert_eq!( connection.get_states()[&Role::Client], h11::State::MightSwitchProtocol ); } assert_eq!( p.conn.get_mut(&Role::Server).unwrap().next_event().unwrap(), Event::Paused {} ); return p; }; // Test deny case let mut p = setup(); p.send(Role::Server, vec![deny.clone()], None).unwrap(); for (_, connection) in &mut p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::Done), (Role::Server, h11::State::SendBody) ]) ); } p.send(Role::Server, vec![EndOfMessage::default().into()], None) .unwrap(); // Check that re-use is still allowed after a denial for (_, connection) in &mut p.conn { connection.start_next_cycle().unwrap(); } // Test accept case let mut p = setup(); p.send(Role::Server, vec![accept.clone()], None).unwrap(); for (_, connection) in &mut p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::SwitchedProtocol), (Role::Server, h11::State::SwitchedProtocol) ]) ); connection.receive_data(b"123").unwrap(); assert_eq!(connection.next_event().unwrap(), Event::Paused {}); connection.receive_data(b"456").unwrap(); assert_eq!(connection.next_event().unwrap(), Event::Paused {}); assert_eq!(connection.get_trailing_data(), (b"123456".to_vec(), false)); } // Pausing in might-switch, then recovery // (weird artificial case where the trailing data actually is valid // HTTP for some reason, because this makes it easier to test the state // logic) let mut p = setup(); let sc = p.conn.get_mut(&Role::Server).unwrap(); sc.receive_data(b"GET / HTTP/1.0\r\n\r\n").unwrap(); assert_eq!(sc.next_event().unwrap(), Event::Paused {}); assert_eq!( sc.get_trailing_data(), (b"GET / HTTP/1.0\r\n\r\n".to_vec(), false) ); sc.send(deny.clone()).unwrap(); assert_eq!(sc.next_event().unwrap(), Event::Paused {}); sc.send(EndOfMessage::default().into()).unwrap(); sc.start_next_cycle().unwrap(); assert_eq!( get_all_events(sc).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/".to_vec(), headers: vec![].into(), http_version: b"1.0".to_vec(), }), Event::EndOfMessage(EndOfMessage::default()), ], ); // When we're DONE, have no trailing data, and the connection gets // closed, we report ConnectionClosed(). When we're in might-switch or // switched, we don't. let mut p = setup(); { let sc = (p.conn).get_mut(&Role::Server).unwrap(); sc.receive_data(b"").unwrap(); assert_eq!(sc.next_event().unwrap(), Event::Paused {}); assert_eq!(sc.get_trailing_data(), (b"".to_vec(), true)); } p.send(Role::Server, vec![accept.clone()], None).unwrap(); assert_eq!( (p.conn) .get_mut(&Role::Server) .unwrap() .next_event() .unwrap(), Event::Paused {} ); let mut p = setup(); let sc = p.conn.get_mut(&Role::Server).unwrap(); sc.receive_data(b"").unwrap(); assert_eq!(sc.next_event().unwrap(), Event::Paused {}); sc.send(deny).unwrap(); assert_eq!( sc.next_event().unwrap(), Event::ConnectionClosed(ConnectionClosed::default()) ); // You can't send after switching protocols, or while waiting for a // protocol switch let mut p = setup(); let cc = p.conn.get_mut(&Role::Client).unwrap(); assert!(match cc.send( Request::new( b"GET".to_vec(), vec![(b"Host".to_vec(), b"a".to_vec())].into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), ) { Err(ProtocolError::LocalProtocolError(_)) => true, _ => false, }); let mut p = setup(); p.send(Role::Server, vec![accept], None).unwrap(); let cc = p.conn.get_mut(&Role::Client).unwrap(); assert!(match cc.send( Data { data: b"123".to_vec(), chunk_start: false, chunk_end: false, } .into(), ) { Err(ProtocolError::LocalProtocolError(_)) => true, _ => false, }); } } // def test_close_simple() -> None: // # Just immediately closing a new connection without anything having // # happened yet. // for who_shot_first, who_shot_second in [(CLIENT, SERVER), (SERVER, CLIENT)]: // def setup() -> ConnectionPair: // p = ConnectionPair() // p.send(who_shot_first, ConnectionClosed()) // for conn in p.conns: // assert conn.states == { // who_shot_first: CLOSED, // who_shot_second: MUST_CLOSE, // } // return p // # You can keep putting b"" into a closed connection, and you keep // # getting ConnectionClosed() out: // p = setup() // assert p.conn[who_shot_second].next_event() == ConnectionClosed() // assert p.conn[who_shot_second].next_event() == ConnectionClosed() // p.conn[who_shot_second].receive_data(b"") // assert p.conn[who_shot_second].next_event() == ConnectionClosed() // # Second party can close... // p = setup() // p.send(who_shot_second, ConnectionClosed()) // for conn in p.conns: // assert conn.our_state is CLOSED // assert conn.their_state is CLOSED // # But trying to receive new data on a closed connection is a // # RuntimeError (not ProtocolError, because the problem here isn't // # violation of HTTP, it's violation of physics) // p = setup() // with pytest.raises(RuntimeError): // p.conn[who_shot_second].receive_data(b"123") // # And receiving new data on a MUST_CLOSE connection is a ProtocolError // p = setup() // p.conn[who_shot_first].receive_data(b"GET") // with pytest.raises(RemoteProtocolError): // p.conn[who_shot_first].next_event() #[test] fn test_close_simple() { // Just immediately closing a new connection without anything having // happened yet. for (who_shot_first, who_shot_second) in vec![(Role::Client, Role::Server), (Role::Server, Role::Client)] { let setup = || { let mut p = ConnectionPair::new(); p.send( who_shot_first, vec![ConnectionClosed::default().into()], None, ) .unwrap(); for (_, connection) in &mut p.conn { assert_eq!( connection.get_states(), HashMap::from([ (who_shot_first, h11::State::Closed), (who_shot_second, h11::State::MustClose) ]) ); } return p; }; // You can keep putting b"" into a closed connection, and you keep // getting ConnectionClosed() out: let mut p = setup(); assert_eq!( p.conn .get_mut(&who_shot_second) .unwrap() .next_event() .unwrap(), Event::ConnectionClosed(ConnectionClosed::default()) ); assert_eq!( p.conn .get_mut(&who_shot_second) .unwrap() .next_event() .unwrap(), Event::ConnectionClosed(ConnectionClosed::default()) ); p.conn .get_mut(&who_shot_second) .unwrap() .receive_data(b"") .unwrap(); assert_eq!( p.conn .get_mut(&who_shot_second) .unwrap() .next_event() .unwrap(), Event::ConnectionClosed(ConnectionClosed::default()) ); // Second party can close... let mut p = setup(); p.send( who_shot_second, vec![ConnectionClosed::default().into()], None, ) .unwrap(); for (_, connection) in &mut p.conn { assert_eq!(connection.get_our_state(), h11::State::Closed); assert_eq!(connection.get_their_state(), h11::State::Closed); } // But trying to receive new data on a closed connection is a // RuntimeError (not ProtocolError, because the problem here isn't // violation of HTTP, it's violation of physics) let mut p = setup(); assert!(match p .conn .get_mut(&who_shot_second) .unwrap() .receive_data(b"123") { Err(message) => true, _ => false, }); // And receiving new data on a MUST_CLOSE connection is a ProtocolError let mut p = setup(); p.conn .get_mut(&who_shot_first) .unwrap() .receive_data(b"GET") .unwrap(); assert!(match p .conn .get_mut(&who_shot_first) .unwrap() .next_event() .unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); } } // def test_close_different_states() -> None: // req = [ // Request(method="GET", target="/foo", headers=[("Host", "a")]), // EndOfMessage(), // ] // resp = [ // Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), // EndOfMessage(), // ] // # Client before request // p = ConnectionPair() // p.send(CLIENT, ConnectionClosed()) // for conn in p.conns: // assert conn.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE} // # Client after request // p = ConnectionPair() // p.send(CLIENT, req) // p.send(CLIENT, ConnectionClosed()) // for conn in p.conns: // assert conn.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE} // # Server after request -> not allowed // p = ConnectionPair() // p.send(CLIENT, req) // with pytest.raises(LocalProtocolError): // p.conn[SERVER].send(ConnectionClosed()) // p.conn[CLIENT].receive_data(b"") // with pytest.raises(RemoteProtocolError): // p.conn[CLIENT].next_event() // # Server after response // p = ConnectionPair() // p.send(CLIENT, req) // p.send(SERVER, resp) // p.send(SERVER, ConnectionClosed()) // for conn in p.conns: // assert conn.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED} // # Both after closing (ConnectionClosed() is idempotent) // p = ConnectionPair() // p.send(CLIENT, req) // p.send(SERVER, resp) // p.send(CLIENT, ConnectionClosed()) // p.send(SERVER, ConnectionClosed()) // p.send(CLIENT, ConnectionClosed()) // p.send(SERVER, ConnectionClosed()) // # In the middle of sending -> not allowed // p = ConnectionPair() // p.send( // CLIENT, // Request( // method="GET", target="/", headers=[("Host", "a"), ("Content-Length", "10")] // ), // ) // with pytest.raises(LocalProtocolError): // p.conn[CLIENT].send(ConnectionClosed()) // p.conn[SERVER].receive_data(b"") // with pytest.raises(RemoteProtocolError): // p.conn[SERVER].next_event() #[test] fn test_close_different_states() { let req: Vec = vec![ Request::new( b"GET".to_vec(), vec![(b"Host".to_vec(), b"a".to_vec())].into(), b"/foo".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), EndOfMessage::default().into(), ]; let resp: Vec = vec![ Response { status_code: 200, headers: vec![(b"transfer-encoding".to_vec(), b"chunked".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), EndOfMessage::default().into(), ]; // Client before request let mut p = ConnectionPair::new(); p.send(Role::Client, vec![ConnectionClosed::default().into()], None) .unwrap(); for (_, connection) in &mut p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::Closed), (Role::Server, h11::State::MustClose) ]) ); } // Client after request let mut p = ConnectionPair::new(); p.send(Role::Client, req.clone(), None).unwrap(); p.send(Role::Client, vec![ConnectionClosed::default().into()], None) .unwrap(); for (_, connection) in &mut p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::Closed), (Role::Server, h11::State::SendResponse) ]) ); } // Server after request -> not allowed let mut p = ConnectionPair::new(); p.send(Role::Client, req.clone(), None).unwrap(); assert!(match p .conn .get_mut(&Role::Server) .unwrap() .send(ConnectionClosed::default().into()) { Err(ProtocolError::LocalProtocolError(_)) => true, _ => false, }); p.conn .get_mut(&Role::Client) .unwrap() .receive_data(b"") .unwrap(); assert!(match p .conn .get_mut(&Role::Client) .unwrap() .next_event() .unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, ProtocolError::LocalProtocolError(m) => panic!("{:?}", m), }); // Server after response let mut p = ConnectionPair::new(); p.send(Role::Client, req.clone(), None).unwrap(); p.send(Role::Server, resp.clone(), None).unwrap(); p.send(Role::Server, vec![ConnectionClosed::default().into()], None) .unwrap(); for (_, connection) in &mut p.conn { assert_eq!( connection.get_states(), HashMap::from([ (Role::Client, h11::State::MustClose), (Role::Server, h11::State::Closed) ]) ); } // Both after closing (ConnectionClosed() is idempotent) let mut p = ConnectionPair::new(); p.send(Role::Client, req.clone(), None).unwrap(); p.send(Role::Server, resp.clone(), None).unwrap(); p.send(Role::Client, vec![ConnectionClosed::default().into()], None) .unwrap(); p.send(Role::Server, vec![ConnectionClosed::default().into()], None) .unwrap(); p.send(Role::Client, vec![ConnectionClosed::default().into()], None) .unwrap(); p.send(Role::Server, vec![ConnectionClosed::default().into()], None) .unwrap(); // In the middle of sending -> not allowed let mut p = ConnectionPair::new(); p.send( Role::Client, vec![Request::new( b"GET".to_vec(), vec![ (b"Host".to_vec(), b"a".to_vec()), (b"Content-Length".to_vec(), b"10".to_vec()), ] .into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into()], None, ) .unwrap(); assert!(match p .conn .get_mut(&Role::Client) .unwrap() .send(ConnectionClosed::default().into()) { Err(ProtocolError::LocalProtocolError(_)) => true, _ => false, }); p.conn .get_mut(&Role::Server) .unwrap() .receive_data(b"") .unwrap(); assert!(match p .conn .get_mut(&Role::Server) .unwrap() .next_event() .unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); } // # Receive several requests and then client shuts down their side of the // # connection; we can respond to each // def test_pipelined_close() -> None: // c = Connection(SERVER) // # 2 requests then a close // c.receive_data( // b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" // b"12345" // b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" // b"67890" // ) // c.receive_data(b"") // assert get_all_events(c) == [ // Request( // method="GET", // target="/1", // headers=[("host", "a.com"), ("content-length", "5")], // ), // Data(data=b"12345"), // EndOfMessage(), // ] // assert c.states[CLIENT] is DONE // c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] // c.send(EndOfMessage()) // assert c.states[SERVER] is DONE // c.start_next_cycle() // assert get_all_events(c) == [ // Request( // method="GET", // target="/2", // headers=[("host", "a.com"), ("content-length", "5")], // ), // Data(data=b"67890"), // EndOfMessage(), // ConnectionClosed(), // ] // assert c.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE} // c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] // c.send(EndOfMessage()) // assert c.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE} // c.send(ConnectionClosed()) // assert c.states == {CLIENT: CLOSED, SERVER: CLOSED} #[test] fn test_pipelined_close() { let mut c = Connection::new(Role::Server, None); // 2 requests then a close c.receive_data( &vec![ b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n".to_vec(), b"12345".to_vec(), b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n".to_vec(), b"67890".to_vec(), ] .into_iter() .flatten() .collect::>(), ) .unwrap(); c.receive_data(b"").unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/1".to_vec(), headers: vec![ (b"Host".to_vec(), b"a.com".to_vec()), (b"Content-Length".to_vec(), b"5".to_vec()) ] .into(), http_version: b"1.1".to_vec(), }), Event::Data(Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, }), Event::EndOfMessage(EndOfMessage::default()), ], ); assert_eq!(c.get_their_state(), h11::State::Done); c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); assert_eq!(c.get_our_state(), h11::State::Done); c.start_next_cycle().unwrap(); assert_eq!( get_all_events(&mut c).unwrap(), vec![ Event::Request(Request { method: b"GET".to_vec(), target: b"/2".to_vec(), headers: vec![ (b"Host".to_vec(), b"a.com".to_vec()), (b"Content-Length".to_vec(), b"5".to_vec()) ] .into(), http_version: b"1.1".to_vec(), }), Event::Data(Data { data: b"67890".to_vec(), chunk_start: false, chunk_end: false, }), Event::EndOfMessage(EndOfMessage::default()), Event::ConnectionClosed(ConnectionClosed::default()), ], ); assert_eq!(c.get_their_state(), h11::State::Closed); assert_eq!(c.get_our_state(), h11::State::SendResponse); c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), ) .unwrap(); c.send(EndOfMessage::default().into()).unwrap(); assert_eq!(c.get_their_state(), h11::State::Closed); assert_eq!(c.get_our_state(), h11::State::MustClose); c.send(ConnectionClosed::default().into()).unwrap(); assert_eq!(c.get_their_state(), h11::State::Closed); assert_eq!(c.get_our_state(), h11::State::Closed); } // def test_errors() -> None: // # After a receive error, you can't receive // for role in [CLIENT, SERVER]: // c = Connection(our_role=role) // c.receive_data(b"gibberish\r\n\r\n") // with pytest.raises(RemoteProtocolError): // c.next_event() // # Now any attempt to receive continues to raise // assert c.their_state is ERROR // assert c.our_state is not ERROR // print(c._cstate.states) // with pytest.raises(RemoteProtocolError): // c.next_event() // # But we can still yell at the client for sending us gibberish // if role is SERVER: // assert ( // c.send(Response(status_code=400, headers=[])) # type: ignore[arg-type] // == b"HTTP/1.1 400 \r\nConnection: close\r\n\r\n" // ) // # After an error sending, you can no longer send // # (This is especially important for things like content-length errors, // # where there's complex internal state being modified) // def conn(role: Type[Sentinel]) -> Connection: // c = Connection(our_role=role) // if role is SERVER: // # Put it into the state where it *could* send a response... // receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") // assert c.our_state is SEND_RESPONSE // return c // for role in [CLIENT, SERVER]: // if role is CLIENT: // # This HTTP/1.0 request won't be detected as bad until after we go // # through the state machine and hit the writing code // good = Request(method="GET", target="/", headers=[("Host", "example.com")]) // bad = Request( // method="GET", // target="/", // headers=[("Host", "example.com")], // http_version="1.0", // ) // elif role is SERVER: // good = Response(status_code=200, headers=[]) # type: ignore[arg-type,assignment] // bad = Response(status_code=200, headers=[], http_version="1.0") # type: ignore[arg-type,assignment] // # Make sure 'good' actually is good // c = conn(role) // c.send(good) // assert c.our_state is not ERROR // # Do that again, but this time sending 'bad' first // c = conn(role) // with pytest.raises(LocalProtocolError): // c.send(bad) // assert c.our_state is ERROR // assert c.their_state is not ERROR // # Now 'good' is not so good // with pytest.raises(LocalProtocolError): // c.send(good) // # And check send_failed() too // c = conn(role) // c.send_failed() // assert c.our_state is ERROR // assert c.their_state is not ERROR // # This is idempotent // c.send_failed() // assert c.our_state is ERROR // assert c.their_state is not ERROR #[test] fn test_errors() { // After a receive error, you can't receive for role in vec![Role::Client, Role::Server] { let mut c = Connection::new(role, None); c.receive_data(b"gibberish\r\n\r\n").unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); // Now any attempt to receive continues to raise assert_eq!(c.get_their_state(), h11::State::Error); assert_ne!(c.get_our_state(), h11::State::Error); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); // But we can still yell at the client for sending us gibberish if role == Role::Server { assert_eq!( c.send( Response { status_code: 400, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into() ) .unwrap() .unwrap(), b"HTTP/1.1 400 \r\nconnection: close\r\n\r\n".to_vec() ); } } // After an error sending, you can no longer send // (This is especially important for things like content-length errors, // where there's complex internal state being modified) let conn = |role: Role| -> Connection { let mut c = Connection::new(role, None); if role == Role::Server { // Put it into the state where it *could* send a response... receive_and_get( &mut c, &b"GET / HTTP/1.0\r\n\r\n" .to_vec() .into_iter() .collect::>(), ) .unwrap(); assert_eq!(c.get_our_state(), h11::State::SendResponse); } return c; }; for role in vec![Role::Client, Role::Server] { let (good, bad): (Event, Event) = if role == Role::Client { // This HTTP/1.0 request won't be detected as bad until after we go // through the state machine and hit the writing code ( Request::new( b"GET".to_vec(), vec![(b"Host".to_vec(), b"example.com".to_vec())].into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), Request::new( b"GET".to_vec(), vec![(b"Host".to_vec(), b"example.com".to_vec())].into(), b"/".to_vec(), b"1.0".to_vec(), ) .unwrap() .into(), ) } else { ( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into(), Response { status_code: 200, headers: vec![].into(), http_version: b"1.0".to_vec(), reason: b"".to_vec(), } .into(), ) }; // Make sure 'good' actually is good let mut c = conn(role); c.send(good.clone()).unwrap(); assert_ne!(c.get_our_state(), h11::State::Error); // Do that again, but this time sending 'bad' first let mut c = conn(role); assert!(match c.send(bad.clone()) { Err(ProtocolError::LocalProtocolError(_)) => true, _ => false, }); assert_eq!(c.get_our_state(), h11::State::Error); assert_ne!(c.get_their_state(), h11::State::Error); // Now 'good' is not so good assert!(match c.send(good.clone()) { Err(ProtocolError::LocalProtocolError(_)) => true, _ => false, }); // And check send_failed() too let mut c = conn(role); c.send_failed(); assert_eq!(c.get_our_state(), h11::State::Error); assert_ne!(c.get_their_state(), h11::State::Error); // This is idempotent c.send_failed(); assert_eq!(c.get_our_state(), h11::State::Error); assert_ne!(c.get_their_state(), h11::State::Error); } } // def test_idle_receive_nothing() -> None: // # At one point this incorrectly raised an error // for role in [CLIENT, SERVER]: // c = Connection(role) // assert c.next_event() is NEED_DATA #[test] fn test_idle_receive_nothing() { // At one point this incorrectly raised an error for role in vec![Role::Client, Role::Server] { let mut c = Connection::new(role, None); assert_eq!(c.next_event().unwrap(), Event::NeedData {}); } } // def test_connection_drop() -> None: // c = Connection(SERVER) // c.receive_data(b"GET /") // assert c.next_event() is NEED_DATA // c.receive_data(b"") // with pytest.raises(RemoteProtocolError): // c.next_event() #[test] fn test_connection_drop() { let mut c = Connection::new(Role::Server, None); c.receive_data(b"GET /").unwrap(); assert_eq!(c.next_event().unwrap(), Event::NeedData {}); c.receive_data(b"").unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); } // def test_408_request_timeout() -> None: // # Should be able to send this spontaneously as a server without seeing // # anything from client // p = ConnectionPair() // p.send(SERVER, Response(status_code=408, headers=[(b"connection", b"close")])) #[test] fn test_408_request_timeout() { // Should be able to send this spontaneously as a server without seeing // anything from client let mut p = ConnectionPair::new(); p.send( Role::Server, vec![Response { status_code: 408, headers: vec![(b"connection".to_vec(), b"close".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into()], None, ) .unwrap(); } // # This used to raise IndexError // def test_empty_request() -> None: // c = Connection(SERVER) // c.receive_data(b"\r\n") // with pytest.raises(RemoteProtocolError): // c.next_event() #[test] fn test_empty_request() { let mut c = Connection::new(Role::Server, None); c.receive_data(b"\r\n").unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); } // # This used to raise IndexError // def test_empty_response() -> None: // c = Connection(CLIENT) // c.send(Request(method="GET", target="/", headers=[("Host", "a")])) // c.receive_data(b"\r\n") // with pytest.raises(RemoteProtocolError): // c.next_event() #[test] fn test_empty_response() { let mut c = Connection::new(Role::Client, None); c.send( Request::new( b"GET".to_vec(), vec![(b"Host".to_vec(), b"a".to_vec())].into(), b"/".to_vec(), b"1.1".to_vec(), ) .unwrap() .into(), ) .unwrap(); c.receive_data(b"\r\n").unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); } // @pytest.mark.parametrize( // "data", // [ // b"\x00", // b"\x20", // b"\x16\x03\x01\x00\xa5", # Typical start of a TLS Client Hello // ], // ) // def test_early_detection_of_invalid_request(data: bytes) -> None: // c = Connection(SERVER) // # Early detection should occur before even receiving a `\r\n` // c.receive_data(data) // with pytest.raises(RemoteProtocolError): // c.next_event() #[test] fn test_early_detection_of_invalid_request() { let data = vec![ b"\x00".to_vec(), b"\x20".to_vec(), b"\x16\x03\x01\x00\xa5".to_vec(), // Typical start of a TLS Client Hello ]; for data in data { let mut c = Connection::new(Role::Server, None); // Early detection should occur before even receiving a `\r\n` c.receive_data(&data).unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); } } // @pytest.mark.parametrize( // "data", // [ // b"\x00", // b"\x20", // b"\x16\x03\x03\x00\x31", # Typical start of a TLS Server Hello // ], // ) // def test_early_detection_of_invalid_response(data: bytes) -> None: // c = Connection(CLIENT) // # Early detection should occur before even receiving a `\r\n` // c.receive_data(data) // with pytest.raises(RemoteProtocolError): // c.next_event() #[test] fn test_early_detection_of_invalid_response() { let data = vec![ b"\x00".to_vec(), b"\x20".to_vec(), b"\x16\x03\x03\x00\x31".to_vec(), // Typical start of a TLS Server Hello ]; for data in data { let mut c = Connection::new(Role::Client, None); // Early detection should occur before even receiving a `\r\n` c.receive_data(&data).unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); } } // # This used to give different headers for HEAD and GET. // # The correct way to handle HEAD is to put whatever headers we *would* have // # put if it were a GET -- even though we know that for HEAD, those headers // # will be ignored. // def test_HEAD_framing_headers() -> None: // def setup(method: bytes, http_version: bytes) -> Connection: // c = Connection(SERVER) // c.receive_data( // method + b" / HTTP/" + http_version + b"\r\n" + b"Host: example.com\r\n\r\n" // ) // assert type(c.next_event()) is Request // assert type(c.next_event()) is EndOfMessage // return c // for method in [b"GET", b"HEAD"]: // # No Content-Length, HTTP/1.1 peer, should use chunked // c = setup(method, b"1.1") // assert ( // c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n" # type: ignore[arg-type] // b"Transfer-Encoding: chunked\r\n\r\n" // ) // # No Content-Length, HTTP/1.0 peer, frame with connection: close // c = setup(method, b"1.0") // assert ( // c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n" # type: ignore[arg-type] // b"Connection: close\r\n\r\n" // ) // # Content-Length + Transfer-Encoding, TE wins // c = setup(method, b"1.1") // assert ( // c.send( // Response( // status_code=200, // headers=[ // ("Content-Length", "100"), // ("Transfer-Encoding", "chunked"), // ], // ) // ) // == b"HTTP/1.1 200 \r\n" // b"Transfer-Encoding: chunked\r\n\r\n" // ) #[test] fn test_head_framing_headers() { let setup = |method: &[u8], http_version: &[u8]| -> Connection { let mut c = Connection::new(Role::Server, None); c.receive_data( &vec![ method.to_vec(), b" / HTTP/".to_vec(), http_version.to_vec(), b"\r\n".to_vec(), b"Host: example.com\r\n\r\n".to_vec(), ] .into_iter() .flatten() .collect::>(), ) .unwrap(); assert!(match c.next_event().unwrap() { Event::Request(_) => true, _ => false, }); assert!(match c.next_event().unwrap() { Event::EndOfMessage(_) => true, _ => false, }); return c; }; for method in vec![b"GET".to_vec(), b"HEAD".to_vec()] { // No Content-Length, HTTP/1.1 peer, should use chunked let mut c = setup(&method, &b"1.1".to_vec()); assert_eq!( c.send( Response { status_code: 200, headers: vec![].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into() ) .unwrap() .unwrap(), b"HTTP/1.1 200 \r\ntransfer-encoding: chunked\r\n\r\n".to_vec() ); // No Content-Length, HTTP/1.0 peer, frame with connection: close let mut c = setup(&method, &b"1.0".to_vec()); assert_eq!( c.send( Response { status_code: 200, headers: vec![(b"connection".to_vec(), b"close".to_vec())].into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into() ) .unwrap() .unwrap(), b"HTTP/1.1 200 \r\nconnection: close\r\n\r\n".to_vec() ); // Content-Length + Transfer-Encoding, TE wins let mut c = setup(&method, &b"1.1".to_vec()); assert_eq!( c.send( Response { status_code: 200, headers: vec![ (b"Content-Length".to_vec(), b"100".to_vec()), (b"Transfer-Encoding".to_vec(), b"chunked".to_vec()) ] .into(), http_version: b"1.1".to_vec(), reason: b"".to_vec(), } .into() ) .unwrap() .unwrap(), b"HTTP/1.1 200 \r\ntransfer-encoding: chunked\r\n\r\n".to_vec() ); } } // def test_special_exceptions_for_lost_connection_in_message_body() -> None: // c = Connection(SERVER) // c.receive_data( // b"POST / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 100\r\n\r\n" // ) // assert type(c.next_event()) is Request // assert c.next_event() is NEED_DATA // c.receive_data(b"12345") // assert c.next_event() == Data(data=b"12345") // c.receive_data(b"") // with pytest.raises(RemoteProtocolError) as excinfo: // c.next_event() // assert "received 5 bytes" in str(excinfo.value) // assert "expected 100" in str(excinfo.value) // c = Connection(SERVER) // c.receive_data( // b"POST / HTTP/1.1\r\n" // b"Host: example.com\r\n" // b"Transfer-Encoding: chunked\r\n\r\n" // ) // assert type(c.next_event()) is Request // assert c.next_event() is NEED_DATA // c.receive_data(b"8\r\n012345") // assert c.next_event().data == b"012345" # type: ignore // c.receive_data(b"") // with pytest.raises(RemoteProtocolError) as excinfo: // c.next_event() // assert "incomplete chunked read" in str(excinfo.value) #[test] fn test_special_exceptions_for_lost_connection_in_message_body() { let mut c = Connection::new(Role::Server, None); c.receive_data( &vec![ b"POST / HTTP/1.1\r\n".to_vec(), b"Host: example.com\r\n".to_vec(), b"Content-Length: 100\r\n\r\n".to_vec(), ] .into_iter() .flatten() .collect::>(), ) .unwrap(); assert!(match c.next_event().unwrap() { Event::Request(_) => true, _ => false, }); assert_eq!(c.next_event().unwrap(), Event::NeedData {}); c.receive_data(b"12345").unwrap(); assert_eq!( c.next_event().unwrap(), Event::Data(Data { data: b"12345".to_vec(), chunk_start: false, chunk_end: false, }) ); c.receive_data(b"").unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); let mut c = Connection::new(Role::Server, None); c.receive_data( &vec![ b"POST / HTTP/1.1\r\n".to_vec(), b"Host: example.com\r\n".to_vec(), b"Transfer-Encoding: chunked\r\n\r\n".to_vec(), ] .into_iter() .flatten() .collect::>(), ) .unwrap(); assert!(match c.next_event().unwrap() { Event::Request(_) => true, _ => false, }); assert_eq!(c.next_event().unwrap(), Event::NeedData {}); c.receive_data(b"8\r\n012345").unwrap(); assert_eq!( match c.next_event().unwrap() { Event::Data(d) => d.data, _ => panic!(), }, b"012345".to_vec() ); c.receive_data(b"").unwrap(); assert!(match c.next_event().unwrap_err() { ProtocolError::RemoteProtocolError(_) => true, _ => false, }); }