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 &mut self.state {
188 State::Handshaking { .. } => {
189 self.close_pending = true;
190 }
191 State::Serving(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 &mut me.state {
214 State::Handshaking { hs, ping_config } => {
215 let mut conn = ready!(Pin::new(hs).poll(cx).map_err(crate::Error::new_h2))?;
216 let ping = if ping_config.is_enabled() {
217 let pp = conn.ping_pong().expect("conn.ping_pong");
218 Some(ping::channel(pp, ping_config.clone(), me.timer.clone()))
219 } else {
220 None
221 };
222 State::Serving(Serving {
223 ping,
224 conn,
225 closing: None,
226 date_header: me.date_header,
227 })
228 }
229 State::Serving(srv) => {
230 if me.close_pending && srv.closing.is_none() {
232 srv.conn.graceful_shutdown();
233 }
234 ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?;
235 return Poll::Ready(Ok(Dispatched::Shutdown));
236 }
237 };
238 me.state = next;
239 }
240 }
241}
242
243impl<T, B> Serving<T, B>
244where
245 T: Read + Write + Unpin,
246 B: Body + 'static,
247{
248 fn poll_server<S, E>(
249 &mut self,
250 cx: &mut Context<'_>,
251 service: &mut S,
252 exec: &mut E,
253 ) -> Poll<crate::Result<()>>
254 where
255 S: HttpService<IncomingBody, ResBody = B>,
256 S::Error: Into<Box<dyn StdError + Send + Sync>>,
257 E: Http2ServerConnExec<S::Future, B>,
258 {
259 if self.closing.is_none() {
260 loop {
261 self.poll_ping(cx);
262
263 match ready!(self.conn.poll_accept(cx)) {
264 Some(Ok((req, mut respond))) => {
265 trace!("incoming request");
266 let content_length = headers::content_length_parse_all(req.headers());
267 let ping = self
268 .ping
269 .as_ref()
270 .map(|ping| ping.0.clone())
271 .unwrap_or_else(ping::disabled);
272
273 ping.record_non_data();
275
276 let is_connect = req.method() == Method::CONNECT;
277 let (mut parts, stream) = req.into_parts();
278 let (mut req, connect_parts) = if !is_connect {
279 (
280 Request::from_parts(
281 parts,
282 IncomingBody::h2(stream, content_length.into(), ping),
283 ),
284 None,
285 )
286 } else {
287 if content_length.map_or(false, |len| len != 0) {
288 warn!("h2 connect request with non-zero body not supported");
289 respond.send_reset(h2::Reason::INTERNAL_ERROR);
290 return Poll::Ready(Ok(()));
291 }
292 let (pending, upgrade) = crate::upgrade::pending();
293 debug_assert!(parts.extensions.get::<OnUpgrade>().is_none());
294 parts.extensions.insert(upgrade);
295 (
296 Request::from_parts(parts, IncomingBody::empty()),
297 Some(ConnectParts {
298 pending,
299 ping,
300 recv_stream: stream,
301 }),
302 )
303 };
304
305 if let Some(protocol) = req.extensions_mut().remove::<h2::ext::Protocol>() {
306 req.extensions_mut().insert(Protocol::from_inner(protocol));
307 }
308
309 let fut = H2Stream::new(
310 service.call(req),
311 connect_parts,
312 respond,
313 self.date_header,
314 exec.clone(),
315 );
316
317 exec.execute_h2stream(fut);
318 }
319 Some(Err(e)) => {
320 return Poll::Ready(Err(crate::Error::new_h2(e)));
321 }
322 None => {
323 if let Some((ping, _)) = &self.ping {
325 ping.ensure_not_timed_out()?;
326 }
327
328 trace!("incoming connection complete");
329 return Poll::Ready(Ok(()));
330 }
331 }
332 }
333 }
334
335 debug_assert!(
336 self.closing.is_some(),
337 "poll_server broke loop without closing"
338 );
339
340 ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;
341
342 Poll::Ready(Err(self.closing.take().expect("polled after error")))
343 }
344
345 fn poll_ping(&mut self, cx: &mut Context<'_>) {
346 if let Some((_, estimator)) = &mut self.ping {
347 match estimator.poll(cx) {
348 Poll::Ready(ping::Ponged::SizeUpdate(wnd)) => {
349 self.conn.set_target_window_size(wnd);
350 let _ = self.conn.set_initial_window_size(wnd);
351 }
352 Poll::Ready(ping::Ponged::KeepAliveTimedOut) => {
353 debug!("keep-alive timed out, closing connection");
354 self.conn.abrupt_shutdown(h2::Reason::NO_ERROR);
355 }
356 Poll::Pending => {}
357 }
358 }
359 }
360}
361
362pin_project! {
363 #[allow(missing_debug_implementations)]
364 pub struct H2Stream<F, B, E>
365 where
366 B: Body,
367 {
368 reply: SendResponse<SendBuf<B::Data>>,
369 #[pin]
370 state: H2StreamState<F, B>,
371 date_header: bool,
372 exec: E,
373 }
374}
375
376pin_project! {
377 #[project = H2StreamStateProj]
378 enum H2StreamState<F, B>
379 where
380 B: Body,
381 {
382 Service {
383 #[pin]
384 fut: F,
385 connect_parts: Option<ConnectParts>,
386 },
387 Body {
388 #[pin]
389 pipe: PipeToSendStream<B>,
390 },
391 }
392}
393
394struct ConnectParts {
395 pending: Pending,
396 ping: Recorder,
397 recv_stream: RecvStream,
398}
399
400impl<F, B, E> H2Stream<F, B, E>
401where
402 B: Body,
403{
404 fn new(
405 fut: F,
406 connect_parts: Option<ConnectParts>,
407 respond: SendResponse<SendBuf<B::Data>>,
408 date_header: bool,
409 exec: E,
410 ) -> H2Stream<F, B, E> {
411 H2Stream {
412 reply: respond,
413 state: H2StreamState::Service { fut, connect_parts },
414 date_header,
415 exec,
416 }
417 }
418}
419
420macro_rules! reply {
421 ($me:expr, $res:expr, $eos:expr) => {{
422 match $me.reply.send_response($res, $eos) {
423 Ok(tx) => tx,
424 Err(e) => {
425 debug!("send response error: {}", e);
426 $me.reply.send_reset(Reason::INTERNAL_ERROR);
427 return Poll::Ready(Err(crate::Error::new_h2(e)));
428 }
429 }
430 }};
431}
432
433impl<F, B, Ex, E> H2Stream<F, B, Ex>
434where
435 F: Future<Output = Result<Response<B>, E>>,
436 B: Body,
437 B::Data: 'static,
438 B::Error: Into<Box<dyn StdError + Send + Sync>>,
439 Ex: Http2UpgradedExec<B::Data>,
440 E: Into<Box<dyn StdError + Send + Sync>>,
441{
442 fn poll2(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<crate::Result<()>> {
443 let mut me = self.as_mut().project();
444 loop {
445 let next = match me.state.as_mut().project() {
446 H2StreamStateProj::Service {
447 fut: h,
448 connect_parts,
449 } => {
450 let res = match h.poll(cx) {
451 Poll::Ready(Ok(r)) => r,
452 Poll::Pending => {
453 if let Poll::Ready(reason) =
456 me.reply.poll_reset(cx).map_err(crate::Error::new_h2)?
457 {
458 debug!("stream received RST_STREAM: {:?}", reason);
459 return Poll::Ready(Err(crate::Error::new_h2(reason.into())));
460 }
461 return Poll::Pending;
462 }
463 Poll::Ready(Err(e)) => {
464 let err = crate::Error::new_user_service(e);
465 warn!("http2 service errored: {}", err);
466 me.reply.send_reset(err.h2_reason());
467 return Poll::Ready(Err(err));
468 }
469 };
470
471 let (head, body) = res.into_parts();
472 let mut res = ::http::Response::from_parts(head, ());
473 super::strip_connection_headers(
474 res.headers_mut(),
475 super::MessageKind::Response,
476 );
477
478 if *me.date_header {
480 res.headers_mut()
481 .entry(::http::header::DATE)
482 .or_insert_with(date::update_and_header_value);
483 }
484
485 if let Some(connect_parts) = connect_parts.take() {
486 if res.status().is_success() {
487 if headers::content_length_parse_all(res.headers())
488 .map_or(false, |len| len != 0)
489 {
490 warn!("h2 successful response to CONNECT request with body not supported");
491 me.reply.send_reset(h2::Reason::INTERNAL_ERROR);
492 return Poll::Ready(Err(crate::Error::new_user_header()));
493 }
494 if res
495 .headers_mut()
496 .remove(::http::header::CONTENT_LENGTH)
497 .is_some()
498 {
499 warn!("successful response to CONNECT request disallows content-length header");
500 }
501 let send_stream = reply!(me, res, false);
502 let (h2_up, up_task) = super::upgrade::pair(
503 send_stream,
504 connect_parts.recv_stream,
505 connect_parts.ping,
506 );
507 connect_parts
508 .pending
509 .fulfill(Upgraded::new(h2_up, Bytes::new()));
510 self.exec.execute_upgrade(up_task);
511 return Poll::Ready(Ok(()));
512 }
513 }
514
515 if !body.is_end_stream() {
516 if let Some(len) = body.size_hint().exact() {
518 headers::set_content_length_if_missing(res.headers_mut(), len);
519 }
520
521 let body_tx = reply!(me, res, false);
522 H2StreamState::Body {
523 pipe: PipeToSendStream::new(body, body_tx),
524 }
525 } else {
526 reply!(me, res, true);
527 return Poll::Ready(Ok(()));
528 }
529 }
530 H2StreamStateProj::Body { pipe } => {
531 return pipe.poll(cx);
532 }
533 };
534 me.state.set(next);
535 }
536 }
537}
538
539impl<F, B, Ex, E> Future for H2Stream<F, B, Ex>
540where
541 F: Future<Output = Result<Response<B>, E>>,
542 B: Body,
543 B::Data: 'static,
544 B::Error: Into<Box<dyn StdError + Send + Sync>>,
545 Ex: Http2UpgradedExec<B::Data>,
546 E: Into<Box<dyn StdError + Send + Sync>>,
547{
548 type Output = ();
549
550 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
551 self.poll2(cx).map(|res| {
552 if let Err(_e) = res {
553 debug!("stream error: {}", _e);
554 }
555 })
556 }
557}