Fix the handling of incoming SETTINGS_INITIAL_WINDOW_SIZE. (#299)
This commit is contained in:
		
				
					committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							78ab6167c4
						
					
				
				
					commit
					d2aa9197f9
				
			| @@ -11,7 +11,7 @@ use futures::{Async, Poll}; | |||||||
| use futures::task::Task; | use futures::task::Task; | ||||||
| use tokio_io::AsyncWrite; | use tokio_io::AsyncWrite; | ||||||
|  |  | ||||||
| use std::{cmp, io}; | use std::io; | ||||||
|  |  | ||||||
| /// Manages state transitions related to outbound frames. | /// Manages state transitions related to outbound frames. | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @@ -370,8 +370,8 @@ impl Send { | |||||||
|             self.init_window_sz = val; |             self.init_window_sz = val; | ||||||
|  |  | ||||||
|             if val < old_val { |             if val < old_val { | ||||||
|  |                 // We must decrease the (remote) window on every open stream. | ||||||
|                 let dec = old_val - val; |                 let dec = old_val - val; | ||||||
|  |  | ||||||
|                 trace!("decrementing all windows; dec={}", dec); |                 trace!("decrementing all windows; dec={}", dec); | ||||||
|  |  | ||||||
|                 let mut total_reclaimed = 0; |                 let mut total_reclaimed = 0; | ||||||
| @@ -380,15 +380,29 @@ impl Send { | |||||||
|  |  | ||||||
|                     stream.send_flow.dec_window(dec); |                     stream.send_flow.dec_window(dec); | ||||||
|  |  | ||||||
|  |                     // It's possible that decreasing the window causes | ||||||
|  |                     // `window_size` (the stream-specific window) to fall below | ||||||
|  |                     // `available` (the portion of the connection-level window | ||||||
|  |                     // that we have allocated to the stream). | ||||||
|  |                     // In this case, we should take that excess allocation away | ||||||
|  |                     // and reassign it to other streams. | ||||||
|  |                     let window_size = stream.send_flow.window_size(); | ||||||
|                     let available = stream.send_flow.available().as_size(); |                     let available = stream.send_flow.available().as_size(); | ||||||
|                     let reclaim = cmp::min(dec, available); |                     let reclaimed = if available > window_size { | ||||||
|                     stream.send_flow.claim_capacity(reclaim); |                         // Drop down to `window_size`. | ||||||
|                     total_reclaimed += reclaim; |                         let reclaim = available - window_size; | ||||||
|  |                         stream.send_flow.claim_capacity(reclaim); | ||||||
|  |                         total_reclaimed += reclaim; | ||||||
|  |                         reclaim | ||||||
|  |                     } else { | ||||||
|  |                         0 | ||||||
|  |                     }; | ||||||
|  |  | ||||||
|                     trace!( |                     trace!( | ||||||
|                         "decremented stream window; id={:?}; decr={}; flow={:?}", |                         "decremented stream window; id={:?}; decr={}; reclaimed={}; flow={:?}", | ||||||
|                         stream.id, |                         stream.id, | ||||||
|                         dec, |                         dec, | ||||||
|  |                         reclaimed, | ||||||
|                         stream.send_flow |                         stream.send_flow | ||||||
|                     ); |                     ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -844,6 +844,63 @@ fn recv_settings_removes_available_capacity() { | |||||||
|         .wait().unwrap(); |         .wait().unwrap(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn recv_settings_keeps_assigned_capacity() { | ||||||
|  |     let _ = ::env_logger::try_init(); | ||||||
|  |     let (io, srv) = mock::new(); | ||||||
|  |  | ||||||
|  |     let (sent_settings, sent_settings_rx) = futures::sync::oneshot::channel(); | ||||||
|  |  | ||||||
|  |     let srv = srv.assert_client_handshake().unwrap() | ||||||
|  |         .recv_settings() | ||||||
|  |         .recv_frame( | ||||||
|  |             frames::headers(1) | ||||||
|  |                 .request("POST", "https://http2.akamai.com/") | ||||||
|  |         ) | ||||||
|  |         .send_frame(frames::settings().initial_window_size(64)) | ||||||
|  |         .recv_frame(frames::settings_ack()) | ||||||
|  |         .then_notify(sent_settings) | ||||||
|  |         .recv_frame(frames::data(1, "hello world").eos()) | ||||||
|  |         .send_frame( | ||||||
|  |             frames::headers(1) | ||||||
|  |                 .response(204) | ||||||
|  |                 .eos() | ||||||
|  |         ) | ||||||
|  |         .close(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     let h2 = client::handshake(io).unwrap() | ||||||
|  |         .and_then(move |(mut client, h2)| { | ||||||
|  |             let request = Request::builder() | ||||||
|  |                 .method(Method::POST) | ||||||
|  |                 .uri("https://http2.akamai.com/") | ||||||
|  |                 .body(()).unwrap(); | ||||||
|  |  | ||||||
|  |             let (response, mut stream) = client.send_request(request, false).unwrap(); | ||||||
|  |  | ||||||
|  |             stream.reserve_capacity(11); | ||||||
|  |  | ||||||
|  |             h2.expect("h2") | ||||||
|  |                 .join( | ||||||
|  |                     util::wait_for_capacity(stream, 11) | ||||||
|  |                         .and_then(|mut stream| { | ||||||
|  |                             sent_settings_rx.expect("rx") | ||||||
|  |                                 .and_then(move |()| { | ||||||
|  |                                     stream.send_data("hello world".into(), true).unwrap(); | ||||||
|  |                                     response.expect("response") | ||||||
|  |                                 }) | ||||||
|  |                                 .and_then(move |resp| { | ||||||
|  |                                     assert_eq!(resp.status(), StatusCode::NO_CONTENT); | ||||||
|  |                                     Ok(client) | ||||||
|  |                                 }) | ||||||
|  |                         }) | ||||||
|  |                 ) | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |     let _ = h2.join(srv) | ||||||
|  |         .wait().unwrap(); | ||||||
|  | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn recv_no_init_window_then_receive_some_init_window() { | fn recv_no_init_window_then_receive_some_init_window() { | ||||||
|     let _ = ::env_logger::try_init(); |     let _ = ::env_logger::try_init(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user