1use std::error::Error as StdError;
2use std::future::Future;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5use std::time::Duration;
6
7use bytes::Bytes;
8use futures_core::ready;
9use h2::server::{Connection, Handshake, SendResponse};
10use h2::{Reason, RecvStream};
11use http::{Method, Request};
12use pin_project_lite::pin_project;
13
14use super::{ping, PipeToSendStream, SendBuf};
15use crate::body::{Body, Incoming as IncomingBody};
16use crate::common::date;
17use crate::common::io::Compat;
18use crate::common::time::Time;
19use crate::ext::Protocol;
20use crate::headers;
21use crate::proto::h2::ping::Recorder;
22use crate::proto::Dispatched;
23use crate::rt::bounds::{Http2ServerConnExec, Http2UpgradedExec};
24use crate::rt::{Read, Write};
25use crate::service::HttpService;
26
27use crate::upgrade::{OnUpgrade, Pending, Upgraded};
28use crate::Response;
29
30const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024; const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024; const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 400; const DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE: u32 = 1024 * 16; const DEFAULT_MAX_LOCAL_ERROR_RESET_STREAMS: usize = 1024;
42
43#[derive(Clone, Debug)]
44pub(crate) struct Config {
45 pub(crate) adaptive_window: bool,
46 pub(crate) initial_conn_window_size: u32,
47 pub(crate) initial_stream_window_size: u32,
48 pub(crate) max_frame_size: u32,
49 pub(crate) enable_connect_protocol: bool,
50 pub(crate) max_concurrent_streams: Option<u32>,
51 pub(crate) max_pending_accept_reset_streams: Option<usize>,
52 pub(crate) max_local_error_reset_streams: Option<usize>,
53 pub(crate) keep_alive_interval: Option<Duration>,
54 pub(crate) keep_alive_timeout: Duration,
55 pub(crate) max_send_buffer_size: usize,
56 pub(crate) header_table_size: Option<u32>,
57 pub(crate) max_header_list_size: u32,
58 pub(crate) date_header: bool,
59}
60
61impl Default for Config {
62 fn default() -> Config {
63 Config {
64 adaptive_window: false,
65 initial_conn_window_size: DEFAULT_CONN_WINDOW,
66 initial_stream_window_size: DEFAULT_STREAM_WINDOW,
67 max_frame_size: DEFAULT_MAX_FRAME_SIZE,
68 enable_connect_protocol: false,
69 max_concurrent_streams: Some(200),
70 max_pending_accept_reset_streams: None,
71 max_local_error_reset_streams: Some(DEFAULT_MAX_LOCAL_ERROR_RESET_STREAMS),
72 header_table_size: None,
73 keep_alive_interval: None,
74 keep_alive_timeout: Duration::from_secs(20),
75 max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE,
76 max_header_list_size: DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE,
77 date_header: true,
78 }
79 }
80}
81
82pin_project! {
83 pub(crate) struct Server<T, S, B, E>
84 where
85 S: HttpService<IncomingBody>,
86 B: Body,
87 {
88 exec: E,
89 timer: Time,
90 service: S,
91 state: State<T, B>,
92 date_header: bool,
93 close_pending: bool
94 }
95}
96
97#[allow(clippy::large_enum_variant)]
99enum State<T, B>
100where
101 B: Body,
102{
103 Handshaking {
104 ping_config: ping::Config,
105 hs: Handshake<Compat<T>, SendBuf<B::Data>>,
106 },
107 Serving(Serving<T, B>),
108}
109
110struct Serving<T, B>
111where
112 B: Body,
113{
114 ping: Option<(ping::Recorder, ping::Ponger)>,
115 conn: Connection<Compat<T>, SendBuf<B::Data>>,
116 closing: Option<crate::Error>,
117 date_header: bool,
118}
119
120impl<T, S, B, E> Server<T, S, B, E>
121where
122 T: Read + Write + Unpin,
123 S: HttpService<IncomingBody, ResBody = B>,
124 S::Error: Into<Box<dyn StdError + Send + Sync>>,
125 B: Body + 'static,
126 E: Http2ServerConnExec<S::Future, B>,
127{
128 pub(crate) fn new(
129 io: T,
130 service: S,
131 config: &Config,
132 exec: E,
133 timer: Time,
134 ) -> Server<T, S, B, E> {
135 let mut builder = h2::server::Builder::default();
136 builder
137 .initial_window_size(config.initial_stream_window_size)
138 .initial_connection_window_size(config.initial_conn_window_size)
139 .max_frame_size(config.max_frame_size)
140 .max_header_list_size(config.max_header_list_size)
141 .max_local_error_reset_streams(config.max_local_error_reset_streams)
142 .max_send_buffer_size(config.max_send_buffer_size);
143 if let Some(max) = config.max_concurrent_streams {
144 builder.max_concurrent_streams(max);
145 }
146 if let Some(max) = config.max_pending_accept_reset_streams {
147 builder.max_pending_accept_reset_streams(max);
148 }
149 if let Some(size) = config.header_table_size {
150 builder.header_table_size(size);
151 }
152 if config.enable_connect_protocol {
153 builder.enable_connect_protocol();
154 }
155 let handshake = builder.handshake(Compat::new(io));
156
157 let bdp = if config.adaptive_window {
158 Some(config.initial_stream_window_size)
159 } else {
160 None
161 };
162
163 let ping_config = ping::Config {
164 bdp_initial_window: bdp,
165 keep_alive_interval: config.keep_alive_interval,
166 keep_alive_timeout: config.keep_alive_timeout,
167 keep_alive_while_idle: true,
170 };
171
172 Server {
173 exec,
174 timer,
175 state: State::Handshaking {
176 ping_config,
177 hs: handshake,
178 },
179 service,
180 date_header: config.date_header,
181 close_pending: false,
182 }
183 }
184
185 pub(crate) fn graceful_shutdown(&mut self) {
186 trace!("graceful_shutdown");
187 match self.state {
188 State::Handshaking { .. } => {
189 self.close_pending = true;
190 }
191 State::Serving(ref mut srv) => {
192 if srv.closing.is_none() {
193 srv.conn.graceful_shutdown();
194 }
195 }
196 }
197 }
198}
199
200impl<T, S, B, E> Future for Server<T, S, B, E>
201where
202 T: Read + Write + Unpin,
203 S: HttpService<IncomingBody, ResBody = B>,
204 S::Error: Into<Box<dyn StdError + Send + Sync>>,
205 B: Body + 'static,
206 E: Http2ServerConnExec<S::Future, B>,
207{
208 type Output = crate::Result<Dispatched>;
209
210 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
211 let me = &mut *self;
212 loop {
213 let next = match me.state {
214 State::Handshaking {
215 ref mut hs,
216 ref ping_config,
217 } => {
218 let mut conn = ready!(Pin::new(hs).poll(cx).map_err(crate::Error::new_h2))?;
219 let ping = if ping_config.is_enabled() {
220 let pp = conn.ping_pong().expect("conn.ping_pong");
221 Some(ping::channel(pp, ping_config.clone(), me.timer.clone()))
222 } else {
223 None
224 };
225 State::Serving(Serving {
226 ping,
227 conn,
228 closing: None,
229 date_header: me.date_header,
230 })
231 }
232 State::Serving(ref mut srv) => {
233 if me.close_pending && srv.closing.is_none() {
235 srv.conn.graceful_shutdown();
236 }
237 ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?;
238 return Poll::Ready(Ok(Dispatched::Shutdown));
239 }
240 };
241 me.state = next;
242 }
243 }
244}
245
246impl<T, B> Serving<T, B>
247where
248 T: Read + Write + Unpin,
249 B: Body + 'static,
250{
251 fn poll_server<S, E>(
252 &mut self,
253 cx: &mut Context<'_>,
254 service: &mut S,
255 exec: &mut E,
256 ) -> Poll<crate::Result<()>>
257 where
258 S: HttpService<IncomingBody, ResBody = B>,
259 S::Error: Into<Box<dyn StdError + Send + Sync>>,
260 E: Http2ServerConnExec<S::Future, B>,
261 {
262 if self.closing.is_none() {
263 loop {
264 self.poll_ping(cx);
265
266 match ready!(self.conn.poll_accept(cx)) {
267 Some(Ok((req, mut respond))) => {
268 trace!("incoming request");
269 let content_length = headers::content_length_parse_all(req.headers());
270 let ping = self
271 .ping
272 .as_ref()
273 .map(|ping| ping.0.clone())
274 .unwrap_or_else(ping::disabled);
275
276 ping.record_non_data();
278
279 let is_connect = req.method() == Method::CONNECT;
280 let (mut parts, stream) = req.into_parts();
281 let (mut req, connect_parts) = if !is_connect {
282 (
283 Request::from_parts(
284 parts,
285 IncomingBody::h2(stream, content_length.into(), ping),
286 ),
287 None,
288 )
289 } else {
290 if content_length.map_or(false, |len| len != 0) {
291 warn!("h2 connect request with non-zero body not supported");
292 respond.send_reset(h2::Reason::INTERNAL_ERROR);
293 return Poll::Ready(Ok(()));
294 }
295 let (pending, upgrade) = crate::upgrade::pending();
296 debug_assert!(parts.extensions.get::<OnUpgrade>().is_none());
297 parts.extensions.insert(upgrade);
298 (
299 Request::from_parts(parts, IncomingBody::empty()),
300 Some(ConnectParts {
301 pending,
302 ping,
303 recv_stream: stream,
304 }),
305 )
306 };
307
308 if let Some(protocol) = req.extensions_mut().remove::<h2::ext::Protocol>() {
309 req.extensions_mut().insert(Protocol::from_inner(protocol));
310 }
311
312 let fut = H2Stream::new(
313 service.call(req),
314 connect_parts,
315 respond,
316 self.date_header,
317 exec.clone(),
318 );
319
320 exec.execute_h2stream(fut);
321 }
322 Some(Err(e)) => {
323 return Poll::Ready(Err(crate::Error::new_h2(e)));
324 }
325 None => {
326 if let Some((ref ping, _)) = self.ping {
328 ping.ensure_not_timed_out()?;
329 }
330
331 trace!("incoming connection complete");
332 return Poll::Ready(Ok(()));
333 }
334 }
335 }
336 }
337
338 debug_assert!(
339 self.closing.is_some(),
340 "poll_server broke loop without closing"
341 );
342
343 ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;
344
345 Poll::Ready(Err(self.closing.take().expect("polled after error")))
346 }
347
348 fn poll_ping(&mut self, cx: &mut Context<'_>) {
349 if let Some((_, ref mut estimator)) = self.ping {
350 match estimator.poll(cx) {
351 Poll::Ready(ping::Ponged::SizeUpdate(wnd)) => {
352 self.conn.set_target_window_size(wnd);
353 let _ = self.conn.set_initial_window_size(wnd);
354 }
355 Poll::Ready(ping::Ponged::KeepAliveTimedOut) => {
356 debug!("keep-alive timed out, closing connection");
357 self.conn.abrupt_shutdown(h2::Reason::NO_ERROR);
358 }
359 Poll::Pending => {}
360 }
361 }
362 }
363}
364
365pin_project! {
366 #[allow(missing_debug_implementations)]
367 pub struct H2Stream<F, B, E>
368 where
369 B: Body,
370 {
371 reply: SendResponse<SendBuf<B::Data>>,
372 #[pin]
373 state: H2StreamState<F, B>,
374 date_header: bool,
375 exec: E,
376 }
377}
378
379pin_project! {
380 #[project = H2StreamStateProj]
381 enum H2StreamState<F, B>
382 where
383 B: Body,
384 {
385 Service {
386 #[pin]
387 fut: F,
388 connect_parts: Option<ConnectParts>,
389 },
390 Body {
391 #[pin]
392 pipe: PipeToSendStream<B>,
393 },
394 }
395}
396
397struct ConnectParts {
398 pending: Pending,
399 ping: Recorder,
400 recv_stream: RecvStream,
401}
402
403impl<F, B, E> H2Stream<F, B, E>
404where
405 B: Body,
406{
407 fn new(
408 fut: F,
409 connect_parts: Option<ConnectParts>,
410 respond: SendResponse<SendBuf<B::Data>>,
411 date_header: bool,
412 exec: E,
413 ) -> H2Stream<F, B, E> {
414 H2Stream {
415 reply: respond,
416 state: H2StreamState::Service { fut, connect_parts },
417 date_header,
418 exec,
419 }
420 }
421}
422
423macro_rules! reply {
424 ($me:expr, $res:expr, $eos:expr) => {{
425 match $me.reply.send_response($res, $eos) {
426 Ok(tx) => tx,
427 Err(e) => {
428 debug!("send response error: {}", e);
429 $me.reply.send_reset(Reason::INTERNAL_ERROR);
430 return Poll::Ready(Err(crate::Error::new_h2(e)));
431 }
432 }
433 }};
434}
435
436impl<F, B, Ex, E> H2Stream<F, B, Ex>
437where
438 F: Future<Output = Result<Response<B>, E>>,
439 B: Body,
440 B::Data: 'static,
441 B::Error: Into<Box<dyn StdError + Send + Sync>>,
442 Ex: Http2UpgradedExec<B::Data>,
443 E: Into<Box<dyn StdError + Send + Sync>>,
444{
445 fn poll2(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<crate::Result<()>> {
446 let mut me = self.as_mut().project();
447 loop {
448 let next = match me.state.as_mut().project() {
449 H2StreamStateProj::Service {
450 fut: h,
451 connect_parts,
452 } => {
453 let res = match h.poll(cx) {
454 Poll::Ready(Ok(r)) => r,
455 Poll::Pending => {
456 if let Poll::Ready(reason) =
459 me.reply.poll_reset(cx).map_err(crate::Error::new_h2)?
460 {
461 debug!("stream received RST_STREAM: {:?}", reason);
462 return Poll::Ready(Err(crate::Error::new_h2(reason.into())));
463 }
464 return Poll::Pending;
465 }
466 Poll::Ready(Err(e)) => {
467 let err = crate::Error::new_user_service(e);
468 warn!("http2 service errored: {}", err);
469 me.reply.send_reset(err.h2_reason());
470 return Poll::Ready(Err(err));
471 }
472 };
473
474 let (head, body) = res.into_parts();
475 let mut res = ::http::Response::from_parts(head, ());
476 super::strip_connection_headers(
477 res.headers_mut(),
478 super::MessageKind::Response,
479 );
480
481 if *me.date_header {
483 res.headers_mut()
484 .entry(::http::header::DATE)
485 .or_insert_with(date::update_and_header_value);
486 }
487
488 if let Some(connect_parts) = connect_parts.take() {
489 if res.status().is_success() {
490 if headers::content_length_parse_all(res.headers())
491 .map_or(false, |len| len != 0)
492 {
493 warn!("h2 successful response to CONNECT request with body not supported");
494 me.reply.send_reset(h2::Reason::INTERNAL_ERROR);
495 return Poll::Ready(Err(crate::Error::new_user_header()));
496 }
497 if res
498 .headers_mut()
499 .remove(::http::header::CONTENT_LENGTH)
500 .is_some()
501 {
502 warn!("successful response to CONNECT request disallows content-length header");
503 }
504 let send_stream = reply!(me, res, false);
505 let (h2_up, up_task) = super::upgrade::pair(
506 send_stream,
507 connect_parts.recv_stream,
508 connect_parts.ping,
509 );
510 connect_parts
511 .pending
512 .fulfill(Upgraded::new(h2_up, Bytes::new()));
513 self.exec.execute_upgrade(up_task);
514 return Poll::Ready(Ok(()));
515 }
516 }
517
518 if !body.is_end_stream() {
519 if let Some(len) = body.size_hint().exact() {
521 headers::set_content_length_if_missing(res.headers_mut(), len);
522 }
523
524 let body_tx = reply!(me, res, false);
525 H2StreamState::Body {
526 pipe: PipeToSendStream::new(body, body_tx),
527 }
528 } else {
529 reply!(me, res, true);
530 return Poll::Ready(Ok(()));
531 }
532 }
533 H2StreamStateProj::Body { pipe } => {
534 return pipe.poll(cx);
535 }
536 };
537 me.state.set(next);
538 }
539 }
540}
541
542impl<F, B, Ex, E> Future for H2Stream<F, B, Ex>
543where
544 F: Future<Output = Result<Response<B>, E>>,
545 B: Body,
546 B::Data: 'static,
547 B::Error: Into<Box<dyn StdError + Send + Sync>>,
548 Ex: Http2UpgradedExec<B::Data>,
549 E: Into<Box<dyn StdError + Send + Sync>>,
550{
551 type Output = ();
552
553 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
554 self.poll2(cx).map(|res| {
555 if let Err(_e) = res {
556 debug!("stream error: {}", _e);
557 }
558 })
559 }
560}