2020-03-07 04:09:31 +02:00
|
|
|
use std::future::Future;
|
2019-01-28 20:41:09 -08:00
|
|
|
|
2021-04-01 15:26:13 +01:00
|
|
|
use actix_utils::future::poll_fn;
|
2019-01-28 20:41:09 -08:00
|
|
|
use bytes::Bytes;
|
2020-03-07 04:09:31 +02:00
|
|
|
use h2::{
|
|
|
|
client::{Builder, Connection, SendRequest},
|
|
|
|
SendStream,
|
|
|
|
};
|
2019-03-18 09:44:48 -07:00
|
|
|
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING};
|
2019-12-05 23:35:43 +06:00
|
|
|
use http::{request::Request, Method, Version};
|
2021-10-26 07:37:40 +08:00
|
|
|
use log::trace;
|
2019-02-06 11:44:15 -08:00
|
|
|
|
2021-10-26 07:37:40 +08:00
|
|
|
use actix_http::{
|
2021-05-05 18:36:02 +01:00
|
|
|
body::{BodySize, MessageBody},
|
|
|
|
header::HeaderMap,
|
2021-12-04 19:40:47 +00:00
|
|
|
Payload, RequestHeadType, ResponseHead,
|
2021-05-05 18:36:02 +01:00
|
|
|
};
|
2019-01-28 20:41:09 -08:00
|
|
|
|
2021-12-04 19:40:47 +00:00
|
|
|
use crate::BoxError;
|
|
|
|
|
2021-05-05 18:36:02 +01:00
|
|
|
use super::{
|
|
|
|
config::ConnectorConfig,
|
|
|
|
connection::{ConnectionIo, H2Connection},
|
|
|
|
error::SendRequestError,
|
|
|
|
};
|
2019-01-28 20:41:09 -08:00
|
|
|
|
2021-03-18 10:53:22 -07:00
|
|
|
pub(crate) async fn send_request<Io, B>(
|
|
|
|
mut io: H2Connection<Io>,
|
2019-09-09 21:29:32 -07:00
|
|
|
head: RequestHeadType,
|
2019-01-28 20:41:09 -08:00
|
|
|
body: B,
|
2019-11-18 18:42:27 +06:00
|
|
|
) -> Result<(ResponseHead, Payload), SendRequestError>
|
2019-01-28 20:41:09 -08:00
|
|
|
where
|
2021-03-18 10:53:22 -07:00
|
|
|
Io: ConnectionIo,
|
2019-01-28 20:41:09 -08:00
|
|
|
B: MessageBody,
|
2021-12-04 19:40:47 +00:00
|
|
|
B::Error: Into<BoxError>,
|
2019-01-28 20:41:09 -08:00
|
|
|
{
|
2019-04-10 12:24:17 -07:00
|
|
|
trace!("Sending client request: {:?} {:?}", head, body.size());
|
2021-02-15 11:24:46 +00:00
|
|
|
|
2019-09-09 21:29:32 -07:00
|
|
|
let head_req = head.as_ref().method == Method::HEAD;
|
2019-04-10 12:24:17 -07:00
|
|
|
let length = body.size();
|
2021-11-16 09:21:10 +00:00
|
|
|
let eof = matches!(length, BodySize::None | BodySize::Sized(0));
|
2019-01-28 20:41:09 -08:00
|
|
|
|
2019-11-18 18:42:27 +06:00
|
|
|
let mut req = Request::new(());
|
|
|
|
*req.uri_mut() = head.as_ref().uri.clone();
|
|
|
|
*req.method_mut() = head.as_ref().method.clone();
|
|
|
|
*req.version_mut() = Version::HTTP_2;
|
2019-02-06 11:44:15 -08:00
|
|
|
|
2019-11-18 18:42:27 +06:00
|
|
|
let mut skip_len = true;
|
|
|
|
// let mut has_date = false;
|
|
|
|
|
|
|
|
// Content length
|
|
|
|
let _ = match length {
|
|
|
|
BodySize::None => None,
|
2021-11-16 09:21:10 +00:00
|
|
|
|
|
|
|
BodySize::Sized(0) => req
|
2019-11-18 18:42:27 +06:00
|
|
|
.headers_mut()
|
|
|
|
.insert(CONTENT_LENGTH, HeaderValue::from_static("0")),
|
2021-11-16 09:21:10 +00:00
|
|
|
|
2021-03-07 19:29:02 +00:00
|
|
|
BodySize::Sized(len) => {
|
|
|
|
let mut buf = itoa::Buffer::new();
|
|
|
|
|
|
|
|
req.headers_mut().insert(
|
|
|
|
CONTENT_LENGTH,
|
|
|
|
HeaderValue::from_str(buf.format(len)).unwrap(),
|
|
|
|
)
|
|
|
|
}
|
2021-11-16 09:21:10 +00:00
|
|
|
|
|
|
|
BodySize::Stream => {
|
|
|
|
skip_len = false;
|
|
|
|
None
|
|
|
|
}
|
2019-11-18 18:42:27 +06:00
|
|
|
};
|
|
|
|
|
|
|
|
// Extracting extra headers from RequestHeadType. HeaderMap::new() does not allocate.
|
|
|
|
let (head, extra_headers) = match head {
|
|
|
|
RequestHeadType::Owned(head) => (RequestHeadType::Owned(head), HeaderMap::new()),
|
|
|
|
RequestHeadType::Rc(head, extra_headers) => (
|
|
|
|
RequestHeadType::Rc(head, None),
|
|
|
|
extra_headers.unwrap_or_else(HeaderMap::new),
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
// merging headers from head and extra headers.
|
|
|
|
let headers = head
|
|
|
|
.as_ref()
|
|
|
|
.headers
|
|
|
|
.iter()
|
|
|
|
.filter(|(name, _)| !extra_headers.contains_key(*name))
|
|
|
|
.chain(extra_headers.iter());
|
|
|
|
|
|
|
|
// copy headers
|
|
|
|
for (key, value) in headers {
|
|
|
|
match *key {
|
2021-03-07 19:29:02 +00:00
|
|
|
// TODO: consider skipping other headers according to:
|
2021-12-02 03:45:04 +00:00
|
|
|
// https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2
|
2021-03-07 19:29:02 +00:00
|
|
|
// omit HTTP/1.x only headers
|
|
|
|
CONNECTION | TRANSFER_ENCODING => continue,
|
2019-11-18 18:42:27 +06:00
|
|
|
CONTENT_LENGTH if skip_len => continue,
|
|
|
|
// DATE => has_date = true,
|
2021-01-04 01:01:35 +00:00
|
|
|
_ => {}
|
2019-11-18 18:42:27 +06:00
|
|
|
}
|
|
|
|
req.headers_mut().append(key, value.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
let res = poll_fn(|cx| io.poll_ready(cx)).await;
|
|
|
|
if let Err(e) = res {
|
2021-03-18 10:53:22 -07:00
|
|
|
io.on_release(e.is_io());
|
2019-11-18 18:42:27 +06:00
|
|
|
return Err(SendRequestError::from(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
let resp = match io.send_request(req, eof) {
|
|
|
|
Ok((fut, send)) => {
|
2021-03-18 10:53:22 -07:00
|
|
|
io.on_release(false);
|
2019-11-18 18:42:27 +06:00
|
|
|
|
|
|
|
if !eof {
|
|
|
|
send_body(body, send).await?;
|
2019-01-28 20:41:09 -08:00
|
|
|
}
|
2019-11-18 18:42:27 +06:00
|
|
|
fut.await.map_err(SendRequestError::from)?
|
|
|
|
}
|
|
|
|
Err(e) => {
|
2021-03-18 10:53:22 -07:00
|
|
|
io.on_release(e.is_io());
|
2019-11-18 18:42:27 +06:00
|
|
|
return Err(e.into());
|
|
|
|
}
|
|
|
|
};
|
2019-01-28 20:41:09 -08:00
|
|
|
|
2019-11-18 18:42:27 +06:00
|
|
|
let (parts, body) = resp.into_parts();
|
|
|
|
let payload = if head_req { Payload::None } else { body.into() };
|
|
|
|
|
|
|
|
let mut head = ResponseHead::new(parts.status);
|
|
|
|
head.version = parts.version;
|
|
|
|
head.headers = parts.headers.into();
|
|
|
|
Ok((head, payload))
|
2019-01-28 20:41:09 -08:00
|
|
|
}
|
|
|
|
|
2021-10-26 07:37:40 +08:00
|
|
|
async fn send_body<B>(body: B, mut send: SendStream<Bytes>) -> Result<(), SendRequestError>
|
2021-05-05 18:36:02 +01:00
|
|
|
where
|
|
|
|
B: MessageBody,
|
2021-12-04 19:40:47 +00:00
|
|
|
B::Error: Into<BoxError>,
|
2021-05-05 18:36:02 +01:00
|
|
|
{
|
2019-11-18 18:42:27 +06:00
|
|
|
let mut buf = None;
|
2021-12-04 19:40:47 +00:00
|
|
|
|
2021-02-16 06:10:22 -08:00
|
|
|
actix_rt::pin!(body);
|
2021-12-04 19:40:47 +00:00
|
|
|
|
2019-11-18 18:42:27 +06:00
|
|
|
loop {
|
|
|
|
if buf.is_none() {
|
2020-01-29 11:15:13 +03:00
|
|
|
match poll_fn(|cx| body.as_mut().poll_next(cx)).await {
|
2019-11-18 18:42:27 +06:00
|
|
|
Some(Ok(b)) => {
|
|
|
|
send.reserve_capacity(b.len());
|
|
|
|
buf = Some(b);
|
|
|
|
}
|
2021-12-04 19:40:47 +00:00
|
|
|
Some(Err(err)) => return Err(SendRequestError::Body(err.into())),
|
2019-11-18 18:42:27 +06:00
|
|
|
None => {
|
2021-12-04 19:40:47 +00:00
|
|
|
if let Err(err) = send.send_data(Bytes::new(), true) {
|
|
|
|
return Err(err.into());
|
2019-01-28 20:41:09 -08:00
|
|
|
}
|
2019-11-18 18:42:27 +06:00
|
|
|
send.reserve_capacity(0);
|
|
|
|
return Ok(());
|
2019-01-28 20:41:09 -08:00
|
|
|
}
|
|
|
|
}
|
2019-11-18 18:42:27 +06:00
|
|
|
}
|
2019-01-28 20:41:09 -08:00
|
|
|
|
2019-11-18 18:42:27 +06:00
|
|
|
match poll_fn(|cx| send.poll_capacity(cx)).await {
|
|
|
|
None => return Ok(()),
|
|
|
|
Some(Ok(cap)) => {
|
|
|
|
let b = buf.as_mut().unwrap();
|
|
|
|
let len = b.len();
|
|
|
|
let bytes = b.split_to(std::cmp::min(cap, len));
|
|
|
|
|
|
|
|
if let Err(e) = send.send_data(bytes, false) {
|
|
|
|
return Err(e.into());
|
2021-06-26 18:33:43 +04:00
|
|
|
}
|
|
|
|
if !b.is_empty() {
|
|
|
|
send.reserve_capacity(b.len());
|
2019-11-18 18:42:27 +06:00
|
|
|
} else {
|
2021-06-26 18:33:43 +04:00
|
|
|
buf = None;
|
2019-01-28 20:41:09 -08:00
|
|
|
}
|
2021-06-26 18:33:43 +04:00
|
|
|
continue;
|
2019-01-28 20:41:09 -08:00
|
|
|
}
|
2019-11-18 18:42:27 +06:00
|
|
|
Some(Err(e)) => return Err(e.into()),
|
2019-01-28 20:41:09 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-18 10:53:22 -07:00
|
|
|
pub(crate) fn handshake<Io: ConnectionIo>(
|
2020-03-07 04:09:31 +02:00
|
|
|
io: Io,
|
|
|
|
config: &ConnectorConfig,
|
2021-10-26 07:37:40 +08:00
|
|
|
) -> impl Future<Output = Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>> {
|
2020-03-07 04:09:31 +02:00
|
|
|
let mut builder = Builder::new();
|
|
|
|
builder
|
|
|
|
.initial_window_size(config.stream_window_size)
|
|
|
|
.initial_connection_window_size(config.conn_window_size)
|
|
|
|
.enable_push(false);
|
|
|
|
builder.handshake(io)
|
|
|
|
}
|