itsi-server 0.2.15 → 0.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Cargo.lock +75 -73
- data/exe/itsi +6 -1
- data/ext/itsi_acme/Cargo.toml +1 -1
- data/ext/itsi_scheduler/Cargo.toml +1 -1
- data/ext/itsi_server/Cargo.lock +1 -1
- data/ext/itsi_server/Cargo.toml +3 -1
- data/ext/itsi_server/extconf.rb +3 -1
- data/ext/itsi_server/src/lib.rs +7 -1
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +2 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +6 -6
- data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +14 -13
- data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +71 -42
- data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +151 -152
- data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +6 -15
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +32 -6
- data/ext/itsi_server/src/ruby_types/itsi_server.rs +1 -1
- data/ext/itsi_server/src/server/binds/listener.rs +49 -8
- data/ext/itsi_server/src/server/frame_stream.rs +142 -0
- data/ext/itsi_server/src/server/http_message_types.rs +143 -10
- data/ext/itsi_server/src/server/io_stream.rs +28 -5
- data/ext/itsi_server/src/server/lifecycle_event.rs +1 -1
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +2 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +8 -10
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +2 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +3 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +54 -58
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +6 -9
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +27 -42
- data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +65 -14
- data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +1 -1
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +8 -11
- data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +21 -8
- data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +2 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +1 -5
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +1 -2
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +13 -6
- data/ext/itsi_server/src/server/mod.rs +1 -0
- data/ext/itsi_server/src/server/process_worker.rs +5 -5
- data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +100 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +87 -31
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +1 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +166 -206
- data/ext/itsi_server/src/server/signal.rs +37 -9
- data/ext/itsi_server/src/server/thread_worker.rs +92 -70
- data/ext/itsi_server/src/services/itsi_http_service.rs +67 -62
- data/ext/itsi_server/src/services/mime_types.rs +185 -183
- data/ext/itsi_server/src/services/rate_limiter.rs +16 -34
- data/ext/itsi_server/src/services/static_file_server.rs +35 -60
- data/lib/itsi/http_request.rb +31 -39
- data/lib/itsi/http_response.rb +5 -0
- data/lib/itsi/rack_env_pool.rb +59 -0
- data/lib/itsi/server/config/config_helpers.rb +1 -2
- data/lib/itsi/server/config/dsl.rb +5 -4
- data/lib/itsi/server/config/middleware/etag.md +3 -7
- data/lib/itsi/server/config/middleware/etag.rb +2 -4
- data/lib/itsi/server/config/middleware/proxy.rb +1 -1
- data/lib/itsi/server/config/middleware/rackup_file.rb +2 -2
- data/lib/itsi/server/config/options/auto_reload_config.rb +6 -2
- data/lib/itsi/server/config/options/include.rb +5 -2
- data/lib/itsi/server/config/options/listen_backlog.rb +1 -1
- data/lib/itsi/server/config/options/pipeline_flush.md +16 -0
- data/lib/itsi/server/config/options/pipeline_flush.rb +19 -0
- data/lib/itsi/server/config/options/send_buffer_size.md +15 -0
- data/lib/itsi/server/config/options/send_buffer_size.rb +19 -0
- data/lib/itsi/server/config/options/writev.md +25 -0
- data/lib/itsi/server/config/options/writev.rb +19 -0
- data/lib/itsi/server/config.rb +43 -31
- data/lib/itsi/server/default_config/Itsi.rb +1 -4
- data/lib/itsi/server/grpc/grpc_call.rb +2 -0
- data/lib/itsi/server/grpc/grpc_interface.rb +2 -2
- data/lib/itsi/server/rack/handler/itsi.rb +3 -1
- data/lib/itsi/server/rack_interface.rb +17 -12
- data/lib/itsi/server/route_tester.rb +1 -1
- data/lib/itsi/server/scheduler_interface.rb +2 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +1 -0
- data/lib/ruby_lsp/itsi/addon.rb +12 -13
- metadata +10 -1
@@ -9,7 +9,7 @@ use super::bind_protocol::BindProtocol;
|
|
9
9
|
use super::tls::ItsiTlsAcceptor;
|
10
10
|
use itsi_error::{ItsiError, Result};
|
11
11
|
use itsi_tracing::info;
|
12
|
-
use socket2::{Domain, Protocol, Socket, Type};
|
12
|
+
use socket2::{Domain, Protocol, SockRef, Socket, Type};
|
13
13
|
use std::fmt::Display;
|
14
14
|
use std::net::{IpAddr, SocketAddr, TcpListener};
|
15
15
|
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
@@ -111,7 +111,7 @@ impl TokioListener {
|
|
111
111
|
Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
|
112
112
|
}
|
113
113
|
|
114
|
-
pub async fn
|
114
|
+
pub async fn spawn_acme_event_task(&self, mut shutdown_receiver: Receiver<RunningPhase>) {
|
115
115
|
if let TokioListener::TcpTls(
|
116
116
|
_,
|
117
117
|
ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
|
@@ -274,15 +274,53 @@ impl Display for Listener {
|
|
274
274
|
}
|
275
275
|
|
276
276
|
impl Listener {
|
277
|
-
pub fn
|
277
|
+
pub fn rebind_listener(listener: TcpListener) -> TcpListener {
|
278
|
+
let sock = SockRef::from(&listener);
|
279
|
+
let (reuse_address, reuse_port) = (
|
280
|
+
sock.reuse_address().unwrap_or(true),
|
281
|
+
sock.reuse_port().unwrap_or(true),
|
282
|
+
);
|
283
|
+
|
284
|
+
if !reuse_address || !reuse_port {
|
285
|
+
return listener;
|
286
|
+
}
|
287
|
+
|
288
|
+
let (ip, port) = sock
|
289
|
+
.local_addr()
|
290
|
+
.unwrap()
|
291
|
+
.as_socket()
|
292
|
+
.map(|addr| (addr.ip(), addr.port()))
|
293
|
+
.unwrap();
|
294
|
+
|
295
|
+
let socket_opts = SocketOpts {
|
296
|
+
reuse_address: sock.reuse_address().unwrap_or(true), // default: true
|
297
|
+
reuse_port: sock.reuse_port().unwrap_or(false), // default: false
|
298
|
+
nodelay: sock.nodelay().unwrap_or(false), // default: false
|
299
|
+
recv_buffer_size: sock.recv_buffer_size().unwrap_or(0),
|
300
|
+
send_buffer_size: sock.send_buffer_size().unwrap_or(0),
|
301
|
+
listen_backlog: 1024, // cannot query – pick sane default
|
302
|
+
};
|
303
|
+
|
304
|
+
connect_tcp_socket(ip, port, &socket_opts).unwrap()
|
305
|
+
}
|
306
|
+
|
307
|
+
pub fn into_tokio_listener(self, no_rebind: bool) -> TokioListener {
|
278
308
|
match self {
|
279
|
-
Listener::Tcp(listener) => {
|
309
|
+
Listener::Tcp(mut listener) => {
|
310
|
+
if cfg!(target_os = "linux") && !no_rebind {
|
311
|
+
listener = Listener::rebind_listener(listener);
|
312
|
+
}
|
280
313
|
TokioListener::Tcp(TokioTcpListener::from_std(listener).unwrap())
|
281
314
|
}
|
282
|
-
Listener::TcpTls((listener, acceptor)) =>
|
283
|
-
|
284
|
-
|
285
|
-
|
315
|
+
Listener::TcpTls((mut listener, acceptor)) => {
|
316
|
+
if cfg!(target_os = "linux") && !no_rebind {
|
317
|
+
listener = Listener::rebind_listener(listener);
|
318
|
+
}
|
319
|
+
TokioListener::TcpTls(
|
320
|
+
TokioTcpListener::from_std(listener).unwrap(),
|
321
|
+
acceptor.clone(),
|
322
|
+
)
|
323
|
+
}
|
286
324
|
Listener::Unix(listener) => {
|
287
325
|
TokioListener::Unix(TokioUnixListener::from_std(listener).unwrap())
|
288
326
|
}
|
@@ -414,6 +452,9 @@ fn connect_tcp_socket(addr: IpAddr, port: u16, socket_opts: &SocketOpts) -> Resu
|
|
414
452
|
socket.set_reuse_port(socket_opts.reuse_port).ok();
|
415
453
|
socket.set_nonblocking(true).ok();
|
416
454
|
socket.set_nodelay(socket_opts.nodelay).ok();
|
455
|
+
socket
|
456
|
+
.set_send_buffer_size(socket_opts.send_buffer_size)
|
457
|
+
.ok();
|
417
458
|
socket
|
418
459
|
.set_recv_buffer_size(socket_opts.recv_buffer_size)
|
419
460
|
.ok();
|
@@ -0,0 +1,142 @@
|
|
1
|
+
use bytes::{Bytes, BytesMut};
|
2
|
+
use futures::Stream;
|
3
|
+
use std::convert::Infallible;
|
4
|
+
use std::future::Future;
|
5
|
+
use std::pin::Pin;
|
6
|
+
use std::task::{Context, Poll};
|
7
|
+
use std::time::Duration;
|
8
|
+
use tokio::sync::mpsc::Receiver;
|
9
|
+
use tokio::sync::watch;
|
10
|
+
use tokio::time::{sleep, Sleep};
|
11
|
+
|
12
|
+
use super::serve_strategy::single_mode::RunningPhase;
|
13
|
+
|
14
|
+
#[derive(Debug)]
|
15
|
+
pub struct FrameStream {
|
16
|
+
receiver: Receiver<Bytes>,
|
17
|
+
shutdown_rx: watch::Receiver<RunningPhase>,
|
18
|
+
drained: bool,
|
19
|
+
}
|
20
|
+
|
21
|
+
impl FrameStream {
|
22
|
+
pub fn new(receiver: Receiver<Bytes>, shutdown_rx: watch::Receiver<RunningPhase>) -> Self {
|
23
|
+
Self {
|
24
|
+
receiver,
|
25
|
+
shutdown_rx,
|
26
|
+
drained: false,
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
impl Stream for FrameStream {
|
32
|
+
type Item = Result<Bytes, Infallible>;
|
33
|
+
|
34
|
+
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
35
|
+
let this = self.get_mut();
|
36
|
+
|
37
|
+
if this.drained {
|
38
|
+
return Poll::Ready(None);
|
39
|
+
}
|
40
|
+
|
41
|
+
match Pin::new(&mut this.receiver).poll_recv(cx) {
|
42
|
+
Poll::Ready(Some(bytes)) => Poll::Ready(Some(Ok(bytes))),
|
43
|
+
Poll::Ready(None) => {
|
44
|
+
this.drained = true;
|
45
|
+
Poll::Ready(None)
|
46
|
+
}
|
47
|
+
Poll::Pending => {
|
48
|
+
if this.shutdown_rx.has_changed().unwrap_or(false)
|
49
|
+
&& *this.shutdown_rx.borrow() == RunningPhase::ShutdownPending
|
50
|
+
{
|
51
|
+
while let Ok(bytes) = this.receiver.try_recv() {
|
52
|
+
return Poll::Ready(Some(Ok(bytes)));
|
53
|
+
}
|
54
|
+
this.drained = true;
|
55
|
+
return Poll::Ready(None);
|
56
|
+
}
|
57
|
+
|
58
|
+
Poll::Pending
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
/// BufferedStream wraps a stream of Bytes and coalesces chunks into a larger buffer,
|
65
|
+
/// flushing either after `max_flush_bytes` is reached or `max_flush_interval` elapses.
|
66
|
+
pub struct BufferedStream<S> {
|
67
|
+
inner: S,
|
68
|
+
buffer: BytesMut,
|
69
|
+
max_flush_bytes: usize,
|
70
|
+
max_flush_interval: Duration,
|
71
|
+
flush_deadline: Option<Pin<Box<Sleep>>>,
|
72
|
+
}
|
73
|
+
|
74
|
+
impl<S> BufferedStream<S> {
|
75
|
+
pub fn new(inner: S, max_flush_bytes: usize, max_flush_interval: Duration) -> Self {
|
76
|
+
Self {
|
77
|
+
inner,
|
78
|
+
buffer: BytesMut::with_capacity(max_flush_bytes),
|
79
|
+
max_flush_bytes,
|
80
|
+
max_flush_interval,
|
81
|
+
flush_deadline: None,
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
impl<S> Stream for BufferedStream<S>
|
87
|
+
where
|
88
|
+
S: Stream<Item = Result<Bytes, Infallible>> + Unpin,
|
89
|
+
{
|
90
|
+
type Item = Result<Bytes, Infallible>;
|
91
|
+
|
92
|
+
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
93
|
+
let this = self.get_mut();
|
94
|
+
|
95
|
+
loop {
|
96
|
+
// Flush on timer if needed
|
97
|
+
if let Some(deadline) = &mut this.flush_deadline {
|
98
|
+
if Pin::new(deadline).poll(cx).is_ready() && !this.buffer.is_empty() {
|
99
|
+
let flushed = this.buffer.split().freeze();
|
100
|
+
this.flush_deadline = None;
|
101
|
+
return Poll::Ready(Some(Ok(flushed)));
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
match Pin::new(&mut this.inner).poll_next(cx) {
|
106
|
+
Poll::Ready(Some(Ok(bytes))) => {
|
107
|
+
this.buffer.extend_from_slice(&bytes);
|
108
|
+
|
109
|
+
if bytes.is_empty() || this.buffer.len() >= this.max_flush_bytes {
|
110
|
+
let flushed = this.buffer.split().freeze();
|
111
|
+
this.flush_deadline = None;
|
112
|
+
return Poll::Ready(Some(Ok(flushed)));
|
113
|
+
}
|
114
|
+
|
115
|
+
if this.flush_deadline.is_none() {
|
116
|
+
this.flush_deadline = Some(Box::pin(sleep(this.max_flush_interval)));
|
117
|
+
}
|
118
|
+
}
|
119
|
+
Poll::Ready(None) => {
|
120
|
+
if this.buffer.is_empty() {
|
121
|
+
return Poll::Ready(None);
|
122
|
+
} else {
|
123
|
+
let flushed = this.buffer.split().freeze();
|
124
|
+
this.flush_deadline = None;
|
125
|
+
return Poll::Ready(Some(Ok(flushed)));
|
126
|
+
}
|
127
|
+
}
|
128
|
+
Poll::Pending => {
|
129
|
+
if let Some(deadline) = &mut this.flush_deadline {
|
130
|
+
let deadline = deadline.as_mut();
|
131
|
+
if deadline.poll(cx).is_ready() && !this.buffer.is_empty() {
|
132
|
+
let flushed = this.buffer.split().freeze();
|
133
|
+
this.flush_deadline = None;
|
134
|
+
return Poll::Ready(Some(Ok(flushed)));
|
135
|
+
}
|
136
|
+
}
|
137
|
+
return Poll::Pending;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
}
|
@@ -1,13 +1,146 @@
|
|
1
|
-
use std::convert::Infallible;
|
2
|
-
|
3
1
|
use bytes::Bytes;
|
4
|
-
use
|
5
|
-
use
|
6
|
-
use
|
2
|
+
use core::fmt;
|
3
|
+
use futures::Stream;
|
4
|
+
use futures_util::TryStreamExt;
|
5
|
+
use http::Request;
|
6
|
+
use http_body_util::{combinators::WithTrailers, BodyExt, Either, Empty, Full, StreamBody};
|
7
|
+
use hyper::body::{Body, Frame, Incoming, SizeHint};
|
8
|
+
use std::{
|
9
|
+
convert::Infallible,
|
10
|
+
pin::Pin,
|
11
|
+
task::{Context, Poll},
|
12
|
+
};
|
7
13
|
|
8
14
|
use super::size_limited_incoming::SizeLimitedIncoming;
|
9
15
|
|
10
|
-
|
16
|
+
type Inner = Either<Full<Bytes>, Empty<Bytes>>;
|
17
|
+
|
18
|
+
type BoxStream =
|
19
|
+
Pin<Box<dyn Stream<Item = Result<Frame<Bytes>, Infallible>> + Send + Sync + 'static>>;
|
20
|
+
|
21
|
+
pub struct PlainBody(Either<StreamBody<BoxStream>, Inner>);
|
22
|
+
|
23
|
+
impl fmt::Debug for PlainBody {
|
24
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
25
|
+
match &self.0 {
|
26
|
+
Either::Left(_) => f.write_str("PlainBody::Stream(..)"),
|
27
|
+
Either::Right(inner) => match inner {
|
28
|
+
Either::Left(full) => f.debug_tuple("PlainBody::Full").field(full).finish(),
|
29
|
+
Either::Right(_) => f.write_str("PlainBody::Empty"),
|
30
|
+
},
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
type DynErr = Box<dyn std::error::Error + Send + Sync>;
|
35
|
+
|
36
|
+
impl Body for PlainBody {
|
37
|
+
type Data = Bytes;
|
38
|
+
type Error = DynErr;
|
39
|
+
|
40
|
+
fn poll_frame(
|
41
|
+
self: Pin<&mut Self>,
|
42
|
+
cx: &mut Context<'_>,
|
43
|
+
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
|
44
|
+
unsafe { self.map_unchecked_mut(|s| &mut s.0) }.poll_frame(cx)
|
45
|
+
}
|
46
|
+
|
47
|
+
fn size_hint(&self) -> SizeHint {
|
48
|
+
self.0.size_hint()
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
impl PlainBody {
|
53
|
+
fn stream<S>(s: S) -> Self
|
54
|
+
where
|
55
|
+
S: Stream<Item = Result<Bytes, Infallible>> + Send + Sync + 'static,
|
56
|
+
{
|
57
|
+
let boxed: BoxStream = Box::pin(s.map_ok(Frame::data));
|
58
|
+
Self(Either::Left(StreamBody::new(boxed)))
|
59
|
+
}
|
60
|
+
|
61
|
+
fn full(bytes: Bytes) -> Self {
|
62
|
+
Self(Either::Right(Either::Left(Full::new(bytes))))
|
63
|
+
}
|
64
|
+
|
65
|
+
fn empty() -> Self {
|
66
|
+
Self(Either::Right(Either::Right(Empty::new())))
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
type BoxTrailers = Pin<
|
71
|
+
Box<dyn std::future::Future<Output = Option<Result<http::HeaderMap, DynErr>>> + Send + Sync>,
|
72
|
+
>;
|
73
|
+
|
74
|
+
pub enum HttpBody {
|
75
|
+
Plain(PlainBody),
|
76
|
+
WithT(WithTrailers<PlainBody, BoxTrailers>),
|
77
|
+
}
|
78
|
+
|
79
|
+
impl fmt::Debug for HttpBody {
|
80
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
81
|
+
match self {
|
82
|
+
HttpBody::Plain(b) => f.debug_tuple("HttpBody::Plain").field(b).finish(),
|
83
|
+
HttpBody::WithT(_) => f.write_str("HttpBody::WithT(..)"),
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
impl Body for HttpBody {
|
89
|
+
type Data = Bytes;
|
90
|
+
type Error = DynErr;
|
91
|
+
|
92
|
+
fn poll_frame(
|
93
|
+
self: Pin<&mut Self>,
|
94
|
+
cx: &mut Context<'_>,
|
95
|
+
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
|
96
|
+
unsafe {
|
97
|
+
match self.get_unchecked_mut() {
|
98
|
+
HttpBody::Plain(b) => Pin::new_unchecked(b).poll_frame(cx),
|
99
|
+
HttpBody::WithT(b) => Pin::new_unchecked(b).poll_frame(cx),
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
fn size_hint(&self) -> SizeHint {
|
105
|
+
match self {
|
106
|
+
HttpBody::Plain(b) => b.size_hint(),
|
107
|
+
HttpBody::WithT(b) => b.size_hint(),
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
impl HttpBody {
|
113
|
+
pub fn stream<S>(s: S) -> Self
|
114
|
+
where
|
115
|
+
S: Stream<Item = Result<Bytes, Infallible>> + Send + Sync + 'static,
|
116
|
+
{
|
117
|
+
HttpBody::Plain(PlainBody::stream(s))
|
118
|
+
}
|
119
|
+
|
120
|
+
pub fn full(bytes: Bytes) -> Self {
|
121
|
+
HttpBody::Plain(PlainBody::full(bytes))
|
122
|
+
}
|
123
|
+
|
124
|
+
pub fn empty() -> Self {
|
125
|
+
HttpBody::Plain(PlainBody::empty())
|
126
|
+
}
|
127
|
+
|
128
|
+
pub fn with_trailers<Fut>(self, fut: Fut) -> Self
|
129
|
+
where
|
130
|
+
Fut: std::future::Future<Output = Option<Result<http::HeaderMap, DynErr>>>
|
131
|
+
+ Send
|
132
|
+
+ Sync
|
133
|
+
+ 'static,
|
134
|
+
{
|
135
|
+
let boxed: BoxTrailers = Box::pin(fut);
|
136
|
+
match self {
|
137
|
+
HttpBody::Plain(p) => HttpBody::WithT(p.with_trailers(boxed)),
|
138
|
+
already @ HttpBody::WithT(_) => already,
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
pub type HttpResponse = http::Response<HttpBody>;
|
11
144
|
pub type HttpRequest = Request<SizeLimitedIncoming<Incoming>>;
|
12
145
|
|
13
146
|
pub trait ConversionExt {
|
@@ -32,7 +165,7 @@ pub trait PathExt {
|
|
32
165
|
fn no_trailing_slash(&self) -> &str;
|
33
166
|
}
|
34
167
|
|
35
|
-
#[derive(Debug, Clone)]
|
168
|
+
#[derive(Debug, Clone, Copy)]
|
36
169
|
pub enum ResponseFormat {
|
37
170
|
JSON,
|
38
171
|
HTML,
|
@@ -64,7 +197,7 @@ impl PathExt for str {
|
|
64
197
|
if self == "/" {
|
65
198
|
self
|
66
199
|
} else {
|
67
|
-
self.trim_end_matches(
|
200
|
+
self.trim_end_matches('/')
|
68
201
|
}
|
69
202
|
}
|
70
203
|
}
|
@@ -91,7 +224,7 @@ impl RequestExt for HttpRequest {
|
|
91
224
|
fn query_param(&self, query_name: &str) -> Option<&str> {
|
92
225
|
self.uri()
|
93
226
|
.query()
|
94
|
-
.and_then(|
|
95
|
-
.map(|
|
227
|
+
.and_then(|q| q.split('&').find(|p| p.starts_with(query_name)))
|
228
|
+
.map(|p| p.split('=').nth(1).unwrap_or(""))
|
96
229
|
}
|
97
230
|
}
|
@@ -2,6 +2,7 @@ use pin_project::pin_project;
|
|
2
2
|
use tokio::net::{TcpStream, UnixStream};
|
3
3
|
use tokio_rustls::server::TlsStream;
|
4
4
|
|
5
|
+
use std::io::{self, IoSlice};
|
5
6
|
use std::os::unix::io::{AsRawFd, RawFd};
|
6
7
|
use std::pin::Pin;
|
7
8
|
use std::task::{Context, Poll};
|
@@ -34,12 +35,12 @@ pub enum IoStream {
|
|
34
35
|
}
|
35
36
|
|
36
37
|
impl IoStream {
|
37
|
-
pub fn addr(&self) ->
|
38
|
+
pub fn addr(&self) -> String {
|
38
39
|
match self {
|
39
|
-
IoStream::Tcp { addr, .. } => addr.
|
40
|
-
IoStream::TcpTls { addr, .. } => addr.
|
41
|
-
IoStream::Unix { addr, .. } => addr.
|
42
|
-
IoStream::UnixTls { addr, .. } => addr.
|
40
|
+
IoStream::Tcp { addr, .. } => addr.to_string(),
|
41
|
+
IoStream::TcpTls { addr, .. } => addr.to_string(),
|
42
|
+
IoStream::Unix { addr, .. } => addr.to_string(),
|
43
|
+
IoStream::UnixTls { addr, .. } => addr.to_string(),
|
43
44
|
}
|
44
45
|
}
|
45
46
|
}
|
@@ -90,6 +91,28 @@ impl AsyncWrite for IoStream {
|
|
90
91
|
IoStreamEnumProj::UnixTls { stream, .. } => stream.poll_shutdown(cx),
|
91
92
|
}
|
92
93
|
}
|
94
|
+
|
95
|
+
fn poll_write_vectored(
|
96
|
+
self: Pin<&mut Self>,
|
97
|
+
cx: &mut Context<'_>,
|
98
|
+
bufs: &[IoSlice<'_>],
|
99
|
+
) -> Poll<io::Result<usize>> {
|
100
|
+
match self.project() {
|
101
|
+
IoStreamEnumProj::Tcp { stream, .. } => stream.poll_write_vectored(cx, bufs),
|
102
|
+
IoStreamEnumProj::TcpTls { stream, .. } => stream.poll_write_vectored(cx, bufs),
|
103
|
+
IoStreamEnumProj::Unix { stream, .. } => stream.poll_write_vectored(cx, bufs),
|
104
|
+
IoStreamEnumProj::UnixTls { stream, .. } => stream.poll_write_vectored(cx, bufs),
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
fn is_write_vectored(&self) -> bool {
|
109
|
+
match self {
|
110
|
+
IoStream::Tcp { stream, .. } => stream.is_write_vectored(),
|
111
|
+
IoStream::TcpTls { stream, .. } => stream.is_write_vectored(),
|
112
|
+
IoStream::Unix { stream, .. } => stream.is_write_vectored(),
|
113
|
+
IoStream::UnixTls { stream, .. } => stream.is_write_vectored(),
|
114
|
+
}
|
115
|
+
}
|
93
116
|
}
|
94
117
|
|
95
118
|
impl AsRawFd for IoStream {
|
@@ -3,7 +3,6 @@ use base64::{engine::general_purpose, Engine};
|
|
3
3
|
use bytes::Bytes;
|
4
4
|
use either::Either;
|
5
5
|
use http::{Response, StatusCode};
|
6
|
-
use http_body_util::{combinators::BoxBody, Full};
|
7
6
|
use magnus::error::Result;
|
8
7
|
use serde::{Deserialize, Serialize};
|
9
8
|
use std::collections::HashMap;
|
@@ -11,7 +10,7 @@ use std::str;
|
|
11
10
|
use tracing::debug;
|
12
11
|
|
13
12
|
use crate::{
|
14
|
-
server::http_message_types::{HttpRequest, HttpResponse, RequestExt},
|
13
|
+
server::http_message_types::{HttpBody, HttpRequest, HttpResponse, RequestExt},
|
15
14
|
services::{itsi_http_service::HttpRequestContext, password_hasher::verify_password_hash},
|
16
15
|
};
|
17
16
|
|
@@ -34,7 +33,7 @@ impl AuthBasic {
|
|
34
33
|
"WWW-Authenticate",
|
35
34
|
format!("Basic realm=\"{}\"", self.realm),
|
36
35
|
)
|
37
|
-
.body(
|
36
|
+
.body(HttpBody::full(Bytes::from("Unauthorized")))
|
38
37
|
.unwrap()
|
39
38
|
}
|
40
39
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
use crate::{
|
2
|
-
server::http_message_types::{HttpRequest, HttpResponse},
|
2
|
+
server::http_message_types::{HttpBody, HttpRequest, HttpResponse},
|
3
3
|
services::itsi_http_service::HttpRequestContext,
|
4
4
|
};
|
5
5
|
|
@@ -20,8 +20,8 @@ use http::{
|
|
20
20
|
header::{GetAll, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TYPE},
|
21
21
|
HeaderValue, Response,
|
22
22
|
};
|
23
|
-
use http_body_util::{
|
24
|
-
use hyper::body::
|
23
|
+
use http_body_util::{BodyExt, StreamBody};
|
24
|
+
use hyper::body::Body;
|
25
25
|
use magnus::error::Result;
|
26
26
|
use serde::{Deserialize, Serialize};
|
27
27
|
use std::convert::Infallible;
|
@@ -126,15 +126,13 @@ impl MimeType {
|
|
126
126
|
}
|
127
127
|
}
|
128
128
|
|
129
|
-
fn stream_encode<R>(encoder: R) ->
|
129
|
+
fn stream_encode<R>(encoder: R) -> HttpBody
|
130
130
|
where
|
131
131
|
R: AsyncRead + Unpin + Sync + Send + 'static,
|
132
132
|
{
|
133
|
-
let encoded_stream = ReaderStream::new(encoder)
|
134
|
-
|
135
|
-
|
136
|
-
});
|
137
|
-
BoxBody::new(StreamBody::new(encoded_stream))
|
133
|
+
let encoded_stream = ReaderStream::new(encoder)
|
134
|
+
.map(|res| res.map_err(|_| -> Infallible { unreachable!("We handle IO errors above") }));
|
135
|
+
HttpBody::stream(StreamBody::new(encoded_stream))
|
138
136
|
}
|
139
137
|
|
140
138
|
fn update_content_encoding(parts: &mut http::response::Parts, new_encoding: HeaderValue) {
|
@@ -293,7 +291,7 @@ impl MiddlewareLayer for Compression {
|
|
293
291
|
}
|
294
292
|
CompressionAlgorithm::Identity => unreachable!(),
|
295
293
|
};
|
296
|
-
|
294
|
+
HttpBody::full(Bytes::from(compressed_bytes))
|
297
295
|
} else {
|
298
296
|
let stream = body
|
299
297
|
.into_data_stream()
|
@@ -1,12 +1,11 @@
|
|
1
1
|
use super::{FromValue, MiddlewareLayer};
|
2
2
|
use crate::{
|
3
|
-
server::http_message_types::{HttpRequest, HttpResponse, RequestExt},
|
3
|
+
server::http_message_types::{HttpBody, HttpRequest, HttpResponse, RequestExt},
|
4
4
|
services::itsi_http_service::HttpRequestContext,
|
5
5
|
};
|
6
6
|
|
7
7
|
use async_trait::async_trait;
|
8
8
|
use http::{HeaderMap, Method, Response};
|
9
|
-
use http_body_util::{combinators::BoxBody, Empty};
|
10
9
|
use itsi_error::ItsiError;
|
11
10
|
use magnus::error::Result;
|
12
11
|
use serde::Deserialize;
|
@@ -273,7 +272,7 @@ impl MiddlewareLayer for Cors {
|
|
273
272
|
let mut response_builder = Response::builder().status(204);
|
274
273
|
*response_builder.headers_mut().unwrap() = headers;
|
275
274
|
let response = response_builder
|
276
|
-
.body(
|
275
|
+
.body(HttpBody::empty())
|
277
276
|
.map_err(ItsiError::new)?;
|
278
277
|
return Ok(either::Either::Right(response));
|
279
278
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
use super::FromValue;
|
2
2
|
use crate::{
|
3
|
-
server::http_message_types::{HttpRequest, HttpResponse},
|
3
|
+
server::http_message_types::{HttpBody, HttpRequest, HttpResponse},
|
4
4
|
services::itsi_http_service::HttpRequestContext,
|
5
5
|
};
|
6
6
|
use async_trait::async_trait;
|
@@ -8,7 +8,7 @@ use bytes::{Bytes, BytesMut};
|
|
8
8
|
use either::Either;
|
9
9
|
use futures::TryStreamExt;
|
10
10
|
use http::{HeaderValue, StatusCode};
|
11
|
-
use http_body_util::
|
11
|
+
use http_body_util::BodyExt;
|
12
12
|
use itsi_error::ItsiError;
|
13
13
|
use serde::{Deserialize, Serialize};
|
14
14
|
use std::sync::Arc;
|
@@ -164,7 +164,7 @@ impl super::MiddlewareLayer for Csp {
|
|
164
164
|
}
|
165
165
|
}
|
166
166
|
|
167
|
-
let mut resp = HttpResponse::new(
|
167
|
+
let mut resp = HttpResponse::new(HttpBody::empty());
|
168
168
|
*resp.status_mut() = StatusCode::NO_CONTENT;
|
169
169
|
return Ok(Either::Right(resp));
|
170
170
|
}
|