itsi 0.2.22 → 0.2.24

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 431fc2c382fd32204fb84de948bfa584a7ad420aff7bd38df80823aef47e6a6e
4
- data.tar.gz: 2f65a4465d5dfe4230d47409426713632eed82de3269a0f54678c558523cf27c
3
+ metadata.gz: 0450be0cae2f0d3822874ee35e312cc258d6adb33bcbdd9fe659c2ed7c368ea9
4
+ data.tar.gz: fc08b8ee180b6241e14dd421e68de982cb3f39e7bf04e791a79ea17a77f99ec3
5
5
  SHA512:
6
- metadata.gz: e9e4b8395a97b8b81180aa83fb19c5bb1f0a15abb80846f29aa933051ac1ff1ac8e20a55244ab60f10eeb35e78ab3703c90073b5bc28da9c4b82b1c3e609224d
7
- data.tar.gz: 9f5a32484e1e861e348dcfd3728479a0b8ea0ed11821f13256ffc64d6af1c8a17d5b2c53545e5b4b29eb5774421098f3d7a41d03b3094937230b8f4f37573f83
6
+ metadata.gz: 8a4b3ed767538405a7aa311dd3c498ee7182072d6e8013d1a620f21abb55012d20b0d919b86ff03798dd8207b94048d8947acf78a32dd641a548e9e947ce2555
7
+ data.tar.gz: 9ce0a92e963013836721b5d9b43a05b4e146d6d96ed122a20554a52d9731e0d805480a207687a3d2734e127b0eccf95276354d31e29a38bbb4b16a34481f3eda
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.2.24] - 2026-04-18
2
+ - Move TLS handshakes out of the listener accept loop so a slow or incomplete TLS client cannot block later HTTPS accepts.
3
+ - Keep malformed proxy-style requests isolated to their connection task instead of disrupting the listener.
4
+
5
+ ## [0.2.23] - 2026-04-18
6
+ - Return a normal 404 for unmatched proxy-style requests such as CONNECT instead of panicking the HTTP listener.
7
+
1
8
  ## [0.2.21] - 2026-02-16
2
9
  - Fix Ruby 2.7 startup NameError in static_assets redirect schema load path
3
10
 
data/Cargo.lock CHANGED
@@ -1662,7 +1662,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1662
1662
 
1663
1663
  [[package]]
1664
1664
  name = "itsi-scheduler"
1665
- version = "0.2.21"
1665
+ version = "0.2.24"
1666
1666
  dependencies = [
1667
1667
  "bytes",
1668
1668
  "derive_more",
@@ -1680,7 +1680,7 @@ dependencies = [
1680
1680
 
1681
1681
  [[package]]
1682
1682
  name = "itsi-server"
1683
- version = "0.2.21"
1683
+ version = "0.2.24"
1684
1684
  dependencies = [
1685
1685
  "argon2",
1686
1686
  "async-channel",
data/Dockerfile CHANGED
@@ -3,7 +3,7 @@ FROM ruby:3.4
3
3
  RUN apt-get update && apt-get install build-essential libclang-dev -y && apt-get clean && rm -rf /var/lib/apt/lists/*
4
4
  RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
5
5
 
6
- COPY pkg/itsi-server-0.2.21.gem .
7
- RUN gem install itsi-server-0.2.21.gem
6
+ COPY pkg/itsi-server-0.2.24.gem .
7
+ RUN gem install itsi-server-0.2.24.gem
8
8
 
9
9
  CMD ["itsi", "serve"]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-scheduler"
3
- version = "0.2.22"
3
+ version = "0.2.24"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -984,7 +984,7 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
984
984
 
985
985
  [[package]]
986
986
  name = "itsi-scheduler"
987
- version = "0.1.0"
987
+ version = "0.2.24"
988
988
  dependencies = [
989
989
  "bytes",
990
990
  "derive_more",
@@ -1002,7 +1002,7 @@ dependencies = [
1002
1002
 
1003
1003
  [[package]]
1004
1004
  name = "itsi-server"
1005
- version = "0.1.0"
1005
+ version = "0.2.24"
1006
1006
  dependencies = [
1007
1007
  "async-channel",
1008
1008
  "async-trait",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-server"
3
- version = "0.2.22"
3
+ version = "0.2.24"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -14,12 +14,13 @@ use std::fmt::Display;
14
14
  use std::net::{IpAddr, SocketAddr, TcpListener};
15
15
  use std::os::fd::{AsRawFd, FromRawFd, RawFd};
16
16
  use std::sync::Arc;
17
+ use std::time::Duration;
17
18
  use std::{os::unix::net::UnixListener, path::PathBuf};
18
19
  use tokio::net::TcpListener as TokioTcpListener;
19
20
  use tokio::net::UnixListener as TokioUnixListener;
20
21
  use tokio::net::{unix, TcpStream, UnixStream};
21
22
  use tokio::sync::watch::Receiver;
22
- use tokio_rustls::TlsAcceptor;
23
+ use tokio::time::timeout;
23
24
  use tokio_stream::StreamExt;
24
25
 
25
26
  pub(crate) enum Listener {
@@ -93,7 +94,7 @@ impl TokioListener {
93
94
  }
94
95
  }
95
96
 
96
- pub(crate) async fn accept(&self) -> Result<IoStream> {
97
+ pub(crate) async fn accept(&self) -> Result<AcceptedStream> {
97
98
  match self {
98
99
  TokioListener::Tcp(listener) => TokioListener::accept_tcp(listener).await,
99
100
  TokioListener::TcpTls(listener, acceptor) => {
@@ -106,9 +107,11 @@ impl TokioListener {
106
107
  }
107
108
  }
108
109
 
109
- async fn accept_tcp(listener: &TokioTcpListener) -> Result<IoStream> {
110
+ async fn accept_tcp(listener: &TokioTcpListener) -> Result<AcceptedStream> {
110
111
  let tcp_stream = listener.accept().await?;
111
- Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
112
+ Ok(AcceptedStream::Ready(Self::to_plain_io(Stream::TcpStream(
113
+ tcp_stream,
114
+ ))))
112
115
  }
113
116
 
114
117
  pub async fn spawn_acme_event_task(&self, mut shutdown_receiver: Receiver<RunningPhase>) {
@@ -137,89 +140,131 @@ impl TokioListener {
137
140
  async fn accept_tls(
138
141
  listener: &TokioTcpListener,
139
142
  acceptor: &ItsiTlsAcceptor,
140
- ) -> Result<IoStream> {
141
- let tcp_stream = listener.accept().await?;
142
- match acceptor {
143
- ItsiTlsAcceptor::Manual(tls_acceptor) => {
144
- Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(tls_acceptor)).await
145
- }
146
- ItsiTlsAcceptor::Automatic(acme_acceptor, _, rustls_config) => {
147
- let accept_future = acme_acceptor.accept(tcp_stream.0);
148
- match accept_future.await {
149
- Ok(None) => Err(ItsiError::Pass),
150
- Ok(Some(start_handshake)) => {
151
- let tls_stream = start_handshake.into_stream(rustls_config.clone()).await?;
152
- Ok(IoStream::TcpTls {
153
- stream: tls_stream,
154
- addr: SockAddr::Tcp(Arc::new(tcp_stream.1)),
155
- })
156
- }
157
- Err(error) => {
158
- error!(error = format!("{:?}", error));
159
- Err(ItsiError::Pass)
160
- }
161
- }
162
- }
163
- }
143
+ ) -> Result<AcceptedStream> {
144
+ let (stream, addr) = listener.accept().await?;
145
+ Ok(AcceptedStream::TcpTls {
146
+ stream,
147
+ addr,
148
+ acceptor: acceptor.clone(),
149
+ })
164
150
  }
165
151
 
166
- async fn accept_unix(listener: &TokioUnixListener) -> Result<IoStream> {
152
+ async fn accept_unix(listener: &TokioUnixListener) -> Result<AcceptedStream> {
167
153
  let unix_stream = listener.accept().await?;
168
- Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
154
+ Ok(AcceptedStream::Ready(Self::to_plain_io(
155
+ Stream::UnixStream(unix_stream),
156
+ )))
169
157
  }
170
158
 
171
159
  async fn accept_unix_tls(
172
160
  listener: &TokioUnixListener,
173
161
  acceptor: &ItsiTlsAcceptor,
174
- ) -> Result<IoStream> {
175
- let unix_stream = listener.accept().await?;
176
- match acceptor {
177
- ItsiTlsAcceptor::Manual(tls_acceptor) => {
178
- Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(tls_acceptor)).await
179
- }
180
- ItsiTlsAcceptor::Automatic(_, _, _) => {
181
- error!("Automatic TLS not supported on Unix sockets");
182
- Err(ItsiError::UnsupportedProtocol(
183
- "Automatic TLS on Unix Sockets".to_owned(),
184
- ))
185
- }
162
+ ) -> Result<AcceptedStream> {
163
+ let (stream, addr) = listener.accept().await?;
164
+ Ok(AcceptedStream::UnixTls {
165
+ stream,
166
+ addr,
167
+ acceptor: acceptor.clone(),
168
+ })
169
+ }
170
+
171
+ fn to_plain_io(input_stream: Stream) -> IoStream {
172
+ match input_stream {
173
+ Stream::TcpStream((tcp_stream, socket_address)) => IoStream::Tcp {
174
+ stream: tcp_stream,
175
+ addr: SockAddr::Tcp(Arc::new(socket_address)),
176
+ },
177
+ Stream::UnixStream((unix_stream, socket_address)) => IoStream::Unix {
178
+ stream: unix_stream,
179
+ addr: SockAddr::Unix(Arc::new(socket_address)),
180
+ },
186
181
  }
187
182
  }
183
+ }
188
184
 
189
- async fn to_tokio_io(
190
- input_stream: Stream,
191
- tls_acceptor: Option<&TlsAcceptor>,
192
- ) -> Result<IoStream> {
193
- match tls_acceptor {
194
- Some(acceptor) => match input_stream {
195
- Stream::TcpStream((tcp_stream, socket_address)) => {
196
- match acceptor.accept(tcp_stream).await {
197
- Ok(tls_stream) => Ok(IoStream::TcpTls {
185
+ pub(crate) enum AcceptedStream {
186
+ Ready(IoStream),
187
+ TcpTls {
188
+ stream: TcpStream,
189
+ addr: SocketAddr,
190
+ acceptor: ItsiTlsAcceptor,
191
+ },
192
+ UnixTls {
193
+ stream: UnixStream,
194
+ addr: unix::SocketAddr,
195
+ acceptor: ItsiTlsAcceptor,
196
+ },
197
+ }
198
+
199
+ impl AcceptedStream {
200
+ pub(crate) async fn into_io_stream(self, handshake_timeout: Duration) -> Result<IoStream> {
201
+ match self {
202
+ AcceptedStream::Ready(stream) => Ok(stream),
203
+ AcceptedStream::TcpTls {
204
+ stream,
205
+ addr,
206
+ acceptor,
207
+ } => match acceptor {
208
+ ItsiTlsAcceptor::Manual(tls_acceptor) => {
209
+ match timeout(handshake_timeout, tls_acceptor.accept(stream)).await {
210
+ Ok(Ok(tls_stream)) => Ok(IoStream::TcpTls {
198
211
  stream: tls_stream,
199
- addr: SockAddr::Tcp(Arc::new(socket_address)),
212
+ addr: SockAddr::Tcp(Arc::new(addr)),
200
213
  }),
201
- Err(err) => Err(err.into()),
214
+ Ok(Err(error)) => Err(error.into()),
215
+ Err(_) => Err(ItsiError::Pass),
202
216
  }
203
217
  }
204
- Stream::UnixStream((unix_stream, socket_address)) => {
205
- match acceptor.accept(unix_stream).await {
206
- Ok(tls_stream) => Ok(IoStream::UnixTls {
218
+ ItsiTlsAcceptor::Automatic(acme_acceptor, _, rustls_config) => {
219
+ let accept_future = acme_acceptor.accept(stream);
220
+ match timeout(handshake_timeout, accept_future).await {
221
+ Err(_) => Err(ItsiError::Pass),
222
+ Ok(accept_result) => match accept_result {
223
+ Ok(None) => Err(ItsiError::Pass),
224
+ Ok(Some(start_handshake)) => {
225
+ match timeout(
226
+ handshake_timeout,
227
+ start_handshake.into_stream(rustls_config.clone()),
228
+ )
229
+ .await
230
+ {
231
+ Ok(Ok(tls_stream)) => Ok(IoStream::TcpTls {
232
+ stream: tls_stream,
233
+ addr: SockAddr::Tcp(Arc::new(addr)),
234
+ }),
235
+ Ok(Err(error)) => Err(error.into()),
236
+ Err(_) => Err(ItsiError::Pass),
237
+ }
238
+ }
239
+ Err(error) => {
240
+ error!(error = format!("{:?}", error));
241
+ Err(ItsiError::Pass)
242
+ }
243
+ },
244
+ }
245
+ }
246
+ },
247
+ AcceptedStream::UnixTls {
248
+ stream,
249
+ addr,
250
+ acceptor,
251
+ } => match acceptor {
252
+ ItsiTlsAcceptor::Manual(tls_acceptor) => {
253
+ match timeout(handshake_timeout, tls_acceptor.accept(stream)).await {
254
+ Ok(Ok(tls_stream)) => Ok(IoStream::UnixTls {
207
255
  stream: tls_stream,
208
- addr: SockAddr::Unix(Arc::new(socket_address)),
256
+ addr: SockAddr::Unix(Arc::new(addr)),
209
257
  }),
210
- Err(err) => Err(err.into()),
258
+ Ok(Err(error)) => Err(error.into()),
259
+ Err(_) => Err(ItsiError::Pass),
211
260
  }
212
261
  }
213
- },
214
- None => match input_stream {
215
- Stream::TcpStream((tcp_stream, socket_address)) => Ok(IoStream::Tcp {
216
- stream: tcp_stream,
217
- addr: SockAddr::Tcp(Arc::new(socket_address)),
218
- }),
219
- Stream::UnixStream((unix_stream, socket_address)) => Ok(IoStream::Unix {
220
- stream: unix_stream,
221
- addr: SockAddr::Unix(Arc::new(socket_address)),
222
- }),
262
+ ItsiTlsAcceptor::Automatic(_, _, _) => {
263
+ error!("Automatic TLS not supported on Unix sockets");
264
+ Err(ItsiError::UnsupportedProtocol(
265
+ "Automatic TLS on Unix Sockets".to_owned(),
266
+ ))
267
+ }
223
268
  },
224
269
  }
225
270
  }
@@ -263,7 +263,7 @@ impl MiddlewareSet {
263
263
  pub fn stack_for(
264
264
  &self,
265
265
  request: &HttpRequest,
266
- ) -> Result<(&Vec<Middleware>, Option<Arc<Regex>>)> {
266
+ ) -> Option<(&Vec<Middleware>, Option<Arc<Regex>>)> {
267
267
  let binding = self.route_set.matches(request.uri().path());
268
268
  let matches = binding.iter();
269
269
 
@@ -276,7 +276,7 @@ impl MiddlewareSet {
276
276
  let matching_pattern = self.patterns.get(index).cloned();
277
277
  if let Some(stack) = self.stacks.get(&index) {
278
278
  if stack.matches(request) {
279
- return Ok((&stack.layers, matching_pattern));
279
+ return Some((&stack.layers, matching_pattern));
280
280
  }
281
281
  }
282
282
  }
@@ -285,13 +285,7 @@ impl MiddlewareSet {
285
285
  request.uri().path(),
286
286
  self.route_set
287
287
  );
288
- Err(magnus::Error::new(
289
- magnus::Ruby::get().unwrap().exception_standard_error(),
290
- format!(
291
- "No matching middleware stack found for request: {:?}",
292
- request
293
- ),
294
- ))
288
+ None
295
289
  }
296
290
 
297
291
  pub fn parse_middleware(middleware_type: String, parameters: Value) -> Result<Middleware> {
@@ -1,11 +1,15 @@
1
1
  use hyper_util::rt::TokioIo;
2
- use std::{ops::Deref, pin::Pin, sync::Arc, time::Duration};
2
+ use std::{future::Future, ops::Deref, pin::Pin, sync::Arc, time::Duration};
3
3
  use tokio::task::JoinSet;
4
4
  use tracing::debug;
5
5
 
6
6
  use crate::{
7
7
  ruby_types::itsi_server::itsi_server_config::ServerParams,
8
- server::{binds::listener::ListenerInfo, io_stream::IoStream, request_job::RequestJob},
8
+ server::{
9
+ binds::listener::{AcceptedStream, ListenerInfo},
10
+ io_stream::IoStream,
11
+ request_job::RequestJob,
12
+ },
9
13
  services::itsi_http_service::{ItsiHttpService, ItsiHttpServiceInner},
10
14
  };
11
15
 
@@ -34,19 +38,39 @@ pub struct AcceptorArgs {
34
38
  }
35
39
 
36
40
  impl Acceptor {
37
- pub(crate) async fn serve_connection(&mut self, stream: IoStream) {
38
- let addr = stream.addr();
39
- let io: TokioIo<Pin<Box<IoStream>>> = TokioIo::new(Box::pin(stream));
41
+ pub(crate) async fn serve_accepted_connection(
42
+ &mut self,
43
+ stream: AcceptedStream,
44
+ tls_handshake_timeout: Duration,
45
+ ) {
46
+ self.spawn_connection(async move { stream.into_io_stream(tls_handshake_timeout).await });
47
+ }
48
+
49
+ fn spawn_connection<F>(&mut self, stream_future: F)
50
+ where
51
+ F: Future<Output = itsi_error::Result<IoStream>> + Send + 'static,
52
+ {
40
53
  let mut shutdown_channel = self.shutdown_receiver.clone();
41
54
  let acceptor_args = self.acceptor_args.clone();
42
- let service = ItsiHttpService {
43
- inner: Arc::new(ItsiHttpServiceInner {
44
- acceptor_args: acceptor_args.clone(),
45
- addr,
46
- }),
47
- };
48
55
 
49
56
  self.join_set.spawn(async move {
57
+ let stream = match stream_future.await {
58
+ Ok(stream) => stream,
59
+ Err(error) => {
60
+ debug!("Connection setup failed: {:?}", error);
61
+ return;
62
+ }
63
+ };
64
+
65
+ let addr = stream.addr();
66
+ let io: TokioIo<Pin<Box<IoStream>>> = TokioIo::new(Box::pin(stream));
67
+ let service = ItsiHttpService {
68
+ inner: Arc::new(ItsiHttpServiceInner {
69
+ acceptor_args: acceptor_args.clone(),
70
+ addr,
71
+ }),
72
+ };
73
+
50
74
  let executor = &acceptor_args.strategy.executor;
51
75
  let svc = hyper::service::service_fn(move |req| {
52
76
  let service = service.clone();
@@ -314,6 +314,7 @@ impl SingleMode {
314
314
 
315
315
  let shutdown_rx_for_acme_task = shutdown_receiver.clone();
316
316
  let acme_task_listener_clone = listener.clone();
317
+ let tls_handshake_timeout = server_params.header_read_timeout;
317
318
 
318
319
  let mut after_accept_wait: Option<Duration> = None::<Duration>;
319
320
 
@@ -337,7 +338,7 @@ impl SingleMode {
337
338
  tokio::select! {
338
339
  accept_result = listener.accept() => {
339
340
  match accept_result {
340
- Ok(accepted) => acceptor.serve_connection(accepted).await,
341
+ Ok(accepted) => acceptor.serve_accepted_connection(accepted, tls_handshake_timeout).await,
341
342
  Err(e) => debug!("Listener.accept failed: {:?}", e)
342
343
  }
343
344
  if cfg!(target_os = "macos") {
@@ -188,14 +188,11 @@ impl ItsiHttpService {
188
188
  let token_preference = self.server_params.itsi_server_token_preference;
189
189
 
190
190
  let service_future = async move {
191
- let middleware_stack = self
192
- .server_params
193
- .middleware
194
- .get()
195
- .unwrap()
196
- .stack_for(&req)
197
- .unwrap();
198
- let (stack, matching_pattern) = middleware_stack;
191
+ let Some((stack, matching_pattern)) =
192
+ self.server_params.middleware.get().unwrap().stack_for(&req)
193
+ else {
194
+ return Ok(NOT_FOUND_RESPONSE.to_http_response(accept).await);
195
+ };
199
196
  let mut resp: Option<HttpResponse> = None;
200
197
 
201
198
  let mut context =
@@ -211,7 +211,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
211
211
 
212
212
  [[package]]
213
213
  name = "itsi-scheduler"
214
- version = "0.2.21"
214
+ version = "0.2.24"
215
215
  dependencies = [
216
216
  "bytes",
217
217
  "derive_more",
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Scheduler
5
- VERSION = "0.2.22"
5
+ VERSION = "0.2.24"
6
6
  end
7
7
  end
@@ -1660,9 +1660,27 @@ version = "1.0.15"
1660
1660
  source = "registry+https://github.com/rust-lang/crates.io-index"
1661
1661
  checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1662
1662
 
1663
+ [[package]]
1664
+ name = "itsi-scheduler"
1665
+ version = "0.2.24"
1666
+ dependencies = [
1667
+ "bytes",
1668
+ "derive_more",
1669
+ "itsi_error",
1670
+ "itsi_instrument_entry",
1671
+ "itsi_rb_helpers",
1672
+ "itsi_tracing",
1673
+ "magnus",
1674
+ "mio",
1675
+ "nix",
1676
+ "parking_lot",
1677
+ "rb-sys",
1678
+ "tracing",
1679
+ ]
1680
+
1663
1681
  [[package]]
1664
1682
  name = "itsi-server"
1665
- version = "0.2.21"
1683
+ version = "0.2.24"
1666
1684
  dependencies = [
1667
1685
  "argon2",
1668
1686
  "async-channel",
@@ -1772,6 +1790,15 @@ dependencies = [
1772
1790
  "thiserror 2.0.12",
1773
1791
  ]
1774
1792
 
1793
+ [[package]]
1794
+ name = "itsi_instrument_entry"
1795
+ version = "0.1.0"
1796
+ dependencies = [
1797
+ "proc-macro2",
1798
+ "quote",
1799
+ "syn 1.0.109",
1800
+ ]
1801
+
1775
1802
  [[package]]
1776
1803
  name = "itsi_rb_helpers"
1777
1804
  version = "0.1.0"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Server
5
- VERSION = "0.2.22"
5
+ VERSION = "0.2.24"
6
6
  end
7
7
  end
@@ -8,6 +8,7 @@ require "itsi/server"
8
8
  require "itsi/scheduler"
9
9
  require "socket"
10
10
  require "net/http"
11
+ require "openssl"
11
12
  require "minitest/autorun"
12
13
 
13
14
  Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
@@ -90,6 +91,25 @@ class RequestContext
90
91
  client.request(request)
91
92
  end
92
93
 
94
+ def raw_http(request)
95
+ socket = TCPSocket.new(@uri.host, @uri.port)
96
+ socket.write(request)
97
+ socket.flush
98
+
99
+ response = +""
100
+ loop do
101
+ ready = IO.select([socket], nil, nil, 1)
102
+ break unless ready
103
+
104
+ response << socket.readpartial(16 * 1024)
105
+ rescue EOFError
106
+ break
107
+ end
108
+ response
109
+ ensure
110
+ socket&.close
111
+ end
112
+
93
113
  def head(path)
94
114
  request = Net::HTTP::Head.new(uri_for(path))
95
115
  client.request(request)
@@ -138,6 +158,7 @@ class RequestContext
138
158
  @uri.host,
139
159
  @uri.port,
140
160
  use_ssl: @uri.scheme == "https",
161
+ verify_mode: OpenSSL::SSL::VERIFY_NONE,
141
162
  **opts
142
163
  )
143
164
  end
@@ -0,0 +1,19 @@
1
+ require_relative "../helpers/test_helper"
2
+
3
+ class TestTlsHandshakeTimeout < Minitest::Test
4
+ def test_incomplete_tls_handshake_does_not_block_listener_indefinitely
5
+ server(
6
+ protocol: "https",
7
+ itsi_rb: lambda do
8
+ header_read_timeout 5.0
9
+ get("/ok") { |r| r.ok "ok" }
10
+ end
11
+ ) do
12
+ socket = TCPSocket.new("127.0.0.1", @uri.port)
13
+
14
+ assert_equal "ok", get("/ok")
15
+ ensure
16
+ socket&.close
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "../helpers/test_helper"
2
+
3
+ class TestUnmatchedRequest < Minitest::Test
4
+ def test_connect_request_without_matching_stack_does_not_stop_listener
5
+ server(
6
+ itsi_rb: lambda do
7
+ get("/ok") { |r| r.respond("ok") }
8
+ end
9
+ ) do
10
+ response = raw_http("CONNECT google.com:443 HTTP/1.1\r\nHost: google.com:443\r\n\r\n")
11
+
12
+ assert_match(/\AHTTP\/\d(?:\.\d)? 404\b/, response)
13
+ assert_equal "ok", get("/ok")
14
+ end
15
+ end
16
+
17
+ def test_origin_form_request_without_leading_slash_does_not_stop_listener
18
+ server(
19
+ itsi_rb: lambda do
20
+ get("/ok") { |r| r.respond("ok") }
21
+ end
22
+ ) do
23
+ response = raw_http("GET default.asp HTTP/1.1\r\nHost: example.com\r\n\r\n")
24
+
25
+ assert_match(/\AHTTP\/\d(?:\.\d)? 404\b/, response)
26
+ assert_equal "ok", get("/ok")
27
+ end
28
+ end
29
+ end
data/lib/itsi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Itsi
2
- VERSION = "0.2.22"
2
+ VERSION = "0.2.24"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: itsi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.22
4
+ version: 0.2.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters
@@ -15,28 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.2.22
18
+ version: 0.2.24
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.2.22
25
+ version: 0.2.24
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: itsi-server
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - '='
31
31
  - !ruby/object:Gem::Version
32
- version: 0.2.22
32
+ version: 0.2.24
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - '='
38
38
  - !ruby/object:Gem::Version
39
- version: 0.2.22
39
+ version: 0.2.24
40
40
  description: Wrapper Gem for both the Itsi server and the Itsi Fiber scheduler
41
41
  email:
42
42
  - wc@pico.net.nz
@@ -1139,6 +1139,8 @@ files:
1139
1139
  - gems/server/test/options/test_request_timeout.rb
1140
1140
  - gems/server/test/options/test_threads.rb
1141
1141
  - gems/server/test/options/test_workers.rb
1142
+ - gems/server/test/options/tls_handshake_timeout.rb
1143
+ - gems/server/test/options/unmatched_request.rb
1142
1144
  - gems/server/test/rack/test_rack_server.rb
1143
1145
  - gems/server/vendor/rb-sys-build/.cargo-ok
1144
1146
  - gems/server/vendor/rb-sys-build/.cargo_vcs_info.json