itsi-server 0.1.7 → 0.1.8

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.
@@ -19,6 +19,7 @@ parking_lot = "0.12.3"
19
19
  rustls-pemfile = "2.2.0"
20
20
  tokio-rustls = "0.26.2"
21
21
  bytes = "1.3"
22
+ tokio-rustls-acme = "0.6.0"
22
23
  rcgen = { version = "0.13.2", features = ["x509-parser", "pem"] }
23
24
  base64 = "0.22.1"
24
25
  http-body-util = "0.1.2"
@@ -39,7 +40,6 @@ httparse = "1.10.1"
39
40
  async-channel = "2.3.1"
40
41
  tempfile = "3.18.0"
41
42
  sysinfo = "0.33.1"
42
- tokio-rustls-acme = "0.6.0"
43
43
  rustls = "0.23.23"
44
44
  fs2 = "0.4.3"
45
45
  ring = "0.17.14"
@@ -6,7 +6,7 @@ use crate::{
6
6
  response::itsi_response::ItsiResponse,
7
7
  server::{
8
8
  itsi_server::{RequestJob, Server},
9
- listener::{SockAddr, TokioListener},
9
+ listener::{ListenerInfo, SockAddr},
10
10
  serve_strategy::single_mode::RunningPhase,
11
11
  },
12
12
  };
@@ -44,7 +44,7 @@ pub struct ItsiRequest {
44
44
  pub remote_addr: String,
45
45
  pub version: String,
46
46
  #[debug(skip)]
47
- pub(crate) listener: Arc<TokioListener>,
47
+ pub(crate) listener: Arc<ListenerInfo>,
48
48
  #[debug(skip)]
49
49
  pub server: Arc<Server>,
50
50
  pub response: ItsiResponse,
@@ -128,7 +128,7 @@ impl ItsiRequest {
128
128
  hyper_request: Request<Incoming>,
129
129
  sender: async_channel::Sender<RequestJob>,
130
130
  server: Arc<Server>,
131
- listener: Arc<TokioListener>,
131
+ listener: Arc<ListenerInfo>,
132
132
  addr: SockAddr,
133
133
  shutdown_rx: watch::Receiver<RunningPhase>,
134
134
  ) -> itsi_error::Result<Response<BoxBody<Bytes, Infallible>>> {
@@ -153,7 +153,7 @@ impl ItsiRequest {
153
153
  request: Request<Incoming>,
154
154
  sock_addr: SockAddr,
155
155
  server: Arc<Server>,
156
- listener: Arc<TokioListener>,
156
+ listener: Arc<ListenerInfo>,
157
157
  ) -> (ItsiRequest, mpsc::Receiver<Option<Bytes>>) {
158
158
  let (parts, body) = request.into_parts();
159
159
  let body = if server.stream_body.is_some_and(|f| f) {
@@ -231,7 +231,7 @@ impl ItsiRequest {
231
231
  .uri
232
232
  .host()
233
233
  .map(|host| host.to_string())
234
- .unwrap_or_else(|| self.listener.host()))
234
+ .unwrap_or_else(|| self.listener.host.clone()))
235
235
  }
236
236
 
237
237
  pub(crate) fn scheme(&self) -> MagnusResult<String> {
@@ -240,7 +240,7 @@ impl ItsiRequest {
240
240
  .uri
241
241
  .scheme()
242
242
  .map(|scheme| scheme.to_string())
243
- .unwrap_or_else(|| self.listener.scheme()))
243
+ .unwrap_or_else(|| self.listener.scheme.clone()))
244
244
  }
245
245
 
246
246
  pub(crate) fn headers(&self) -> MagnusResult<Vec<(String, &str)>> {
@@ -264,7 +264,7 @@ impl ItsiRequest {
264
264
  }
265
265
 
266
266
  pub(crate) fn port(&self) -> MagnusResult<u16> {
267
- Ok(self.parts.uri.port_u16().unwrap_or(self.listener.port()))
267
+ Ok(self.parts.uri.port_u16().unwrap_or(self.listener.port))
268
268
  }
269
269
 
270
270
  pub(crate) fn body(&self) -> MagnusResult<Value> {
@@ -9,6 +9,7 @@ use std::{
9
9
  path::PathBuf,
10
10
  str::FromStr,
11
11
  };
12
+
12
13
  #[derive(Debug, Clone)]
13
14
  pub enum BindAddress {
14
15
  Ip(IpAddr),
@@ -129,13 +130,13 @@ impl FromStr for Bind {
129
130
  BindProtocol::Unix => None,
130
131
  BindProtocol::Unixs => Some(configure_tls(host, &options)?),
131
132
  };
132
-
133
- Ok(Self {
133
+ let bind = Self {
134
134
  address,
135
135
  port,
136
136
  protocol,
137
137
  tls_config,
138
- })
138
+ };
139
+ Ok(bind)
139
140
  }
140
141
  }
141
142
 
@@ -19,7 +19,7 @@ use parking_lot::Mutex;
19
19
  use std::{cmp::max, ops::Deref, sync::Arc};
20
20
  use tracing::{info, instrument};
21
21
 
22
- static DEFAULT_BIND: &str = "localhost:3000";
22
+ static DEFAULT_BIND: &str = "http://localhost:3000";
23
23
 
24
24
  #[magnus::wrap(class = "Itsi::Server", free_immediately, size)]
25
25
  #[derive(Clone)]
@@ -231,13 +231,6 @@ impl Server {
231
231
  drop(strategy);
232
232
  Ok(())
233
233
  })?;
234
- if let Ok(listeners) = Arc::try_unwrap(listeners) {
235
- listeners.into_iter().for_each(|listener| {
236
- if let Ok(listener) = Arc::try_unwrap(listener) {
237
- listener.unbind()
238
- };
239
- });
240
- }
241
234
  clear_signal_handlers();
242
235
  Ok(())
243
236
  }
@@ -1,6 +1,7 @@
1
1
  use super::bind::{Bind, BindAddress};
2
2
  use super::bind_protocol::BindProtocol;
3
3
  use super::io_stream::IoStream;
4
+ use super::serve_strategy::single_mode::RunningPhase;
4
5
  use super::tls::ItsiTlsAcceptor;
5
6
  use itsi_error::{ItsiError, Result};
6
7
  use itsi_tracing::info;
@@ -11,6 +12,7 @@ use std::{os::unix::net::UnixListener, path::PathBuf};
11
12
  use tokio::net::TcpListener as TokioTcpListener;
12
13
  use tokio::net::UnixListener as TokioUnixListener;
13
14
  use tokio::net::{unix, TcpStream, UnixStream};
15
+ use tokio::sync::watch::Receiver;
14
16
  use tokio_rustls::TlsAcceptor;
15
17
  use tokio_stream::StreamExt;
16
18
  use tracing::error;
@@ -23,45 +25,79 @@ pub(crate) enum Listener {
23
25
  }
24
26
 
25
27
  pub(crate) enum TokioListener {
26
- Tcp {
27
- listener: TokioTcpListener,
28
- host: String,
29
- port: u16,
30
- },
31
- TcpTls {
32
- listener: TokioTcpListener,
33
- acceptor: ItsiTlsAcceptor,
34
- host: String,
35
- port: u16,
36
- },
37
- Unix {
38
- listener: TokioUnixListener,
39
- },
40
- UnixTls {
41
- listener: TokioUnixListener,
42
- acceptor: ItsiTlsAcceptor,
43
- },
28
+ Tcp(TokioTcpListener),
29
+ TcpTls(TokioTcpListener, ItsiTlsAcceptor),
30
+ Unix(TokioUnixListener),
31
+ UnixTls(TokioUnixListener, ItsiTlsAcceptor),
32
+ }
33
+
34
+ #[derive(Debug, Clone)]
35
+ pub struct ListenerInfo {
36
+ pub host: String,
37
+ pub port: u16,
38
+ pub scheme: String,
44
39
  }
45
40
 
46
41
  impl TokioListener {
47
- pub fn unbind(self) {
42
+ pub fn listener_info(&self) -> ListenerInfo {
48
43
  match self {
49
- TokioListener::Tcp { listener, .. } => drop(listener.into_std().unwrap()),
50
- TokioListener::TcpTls { listener, .. } => drop(listener.into_std().unwrap()),
51
- TokioListener::Unix { listener } => drop(listener.into_std().unwrap()),
52
- TokioListener::UnixTls { listener, .. } => drop(listener.into_std().unwrap()),
53
- };
44
+ TokioListener::Tcp(listener) => ListenerInfo {
45
+ host: listener
46
+ .local_addr()
47
+ .unwrap()
48
+ .ip()
49
+ .to_canonical()
50
+ .to_string(),
51
+ port: listener.local_addr().unwrap().port(),
52
+ scheme: "http".to_string(),
53
+ },
54
+ TokioListener::TcpTls(listener, _) => ListenerInfo {
55
+ host: listener
56
+ .local_addr()
57
+ .unwrap()
58
+ .ip()
59
+ .to_canonical()
60
+ .to_string(),
61
+ port: listener.local_addr().unwrap().port(),
62
+ scheme: "https".to_string(),
63
+ },
64
+ TokioListener::Unix(listener) => ListenerInfo {
65
+ host: listener
66
+ .local_addr()
67
+ .unwrap()
68
+ .as_pathname()
69
+ .unwrap()
70
+ .to_str()
71
+ .unwrap()
72
+ .to_owned(),
73
+ port: 0,
74
+ scheme: "unix".to_string(),
75
+ },
76
+ TokioListener::UnixTls(listener, _) => ListenerInfo {
77
+ host: listener
78
+ .local_addr()
79
+ .unwrap()
80
+ .as_pathname()
81
+ .unwrap()
82
+ .to_str()
83
+ .unwrap()
84
+ .to_owned(),
85
+ port: 0,
86
+ scheme: "ssl".to_string(),
87
+ },
88
+ }
54
89
  }
90
+
55
91
  pub(crate) async fn accept(&self) -> Result<IoStream> {
56
92
  match self {
57
- TokioListener::Tcp { listener, .. } => TokioListener::accept_tcp(listener).await,
58
- TokioListener::TcpTls {
59
- listener, acceptor, ..
60
- } => TokioListener::accept_tls(listener, acceptor).await,
61
- TokioListener::Unix { listener, .. } => TokioListener::accept_unix(listener).await,
62
- TokioListener::UnixTls {
63
- listener, acceptor, ..
64
- } => TokioListener::accept_unix_tls(listener, acceptor).await,
93
+ TokioListener::Tcp(listener) => TokioListener::accept_tcp(listener).await,
94
+ TokioListener::TcpTls(listener, acceptor) => {
95
+ TokioListener::accept_tls(listener, acceptor).await
96
+ }
97
+ TokioListener::Unix(listener) => TokioListener::accept_unix(listener).await,
98
+ TokioListener::UnixTls(listener, acceptor) => {
99
+ TokioListener::accept_unix_tls(listener, acceptor).await
100
+ }
65
101
  }
66
102
  }
67
103
 
@@ -70,17 +106,24 @@ impl TokioListener {
70
106
  Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
71
107
  }
72
108
 
73
- pub async fn spawn_state_task(&self) {
74
- if let TokioListener::TcpTls {
75
- acceptor: ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
76
- ..
77
- } = self
109
+ pub async fn spawn_state_task(&self, mut shutdown_receiver: Receiver<RunningPhase>) {
110
+ if let TokioListener::TcpTls(
111
+ _,
112
+ ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
113
+ ) = self
78
114
  {
79
115
  let mut state = state.lock().await;
80
116
  loop {
81
- match StreamExt::next(&mut *state).await {
82
- Some(event) => info!("Received acme event: {:?}", event),
83
- None => error!("Received no acme event"),
117
+ tokio::select! {
118
+ stream_event = StreamExt::next(&mut *state) => {
119
+ match stream_event {
120
+ Some(event) => info!("Received acme event: {:?}", event),
121
+ None => error!("Received no acme event"),
122
+ }
123
+ },
124
+ _ = shutdown_receiver.changed() => {
125
+ break;
126
+ }
84
127
  }
85
128
  }
86
129
  }
@@ -175,33 +218,6 @@ impl TokioListener {
175
218
  },
176
219
  }
177
220
  }
178
-
179
- pub(crate) fn scheme(&self) -> String {
180
- match self {
181
- TokioListener::Tcp { .. } => "http".to_string(),
182
- TokioListener::TcpTls { .. } => "https".to_string(),
183
- TokioListener::Unix { .. } => "http".to_string(),
184
- TokioListener::UnixTls { .. } => "https".to_string(),
185
- }
186
- }
187
-
188
- pub(crate) fn port(&self) -> u16 {
189
- match self {
190
- TokioListener::Tcp { port, .. } => *port,
191
- TokioListener::TcpTls { port, .. } => *port,
192
- TokioListener::Unix { .. } => 0,
193
- TokioListener::UnixTls { .. } => 0,
194
- }
195
- }
196
-
197
- pub(crate) fn host(&self) -> String {
198
- match self {
199
- TokioListener::Tcp { host, .. } => host.to_string(),
200
- TokioListener::TcpTls { host, .. } => host.to_string(),
201
- TokioListener::Unix { .. } => "unix".to_string(),
202
- TokioListener::UnixTls { .. } => "unix".to_string(),
203
- }
204
- }
205
221
  }
206
222
 
207
223
  enum Stream {
@@ -227,48 +243,22 @@ impl std::fmt::Display for SockAddr {
227
243
  }
228
244
 
229
245
  impl Listener {
230
- pub fn unbind(self) {
231
- match self {
232
- Listener::Tcp(listener) => drop(listener),
233
- Listener::TcpTls((listener, _)) => drop(listener),
234
- Listener::Unix(listener) => drop(listener),
235
- Listener::UnixTls((listener, _)) => drop(listener),
236
- };
237
- }
238
246
  pub fn to_tokio_listener(&self) -> TokioListener {
239
247
  match self {
240
- Listener::Tcp(listener) => TokioListener::Tcp {
241
- listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
242
- .unwrap(),
243
- host: listener
244
- .local_addr()
245
- .unwrap()
246
- .ip()
247
- .to_canonical()
248
- .to_string(),
249
- port: listener.local_addr().unwrap().port(),
250
- },
251
- Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls {
252
- listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
253
- .unwrap(),
254
- acceptor: acceptor.clone(),
255
- host: listener
256
- .local_addr()
257
- .unwrap()
258
- .ip()
259
- .to_canonical()
260
- .to_string(),
261
- port: listener.local_addr().unwrap().port(),
262
- },
263
- Listener::Unix(listener) => TokioListener::Unix {
264
- listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
265
- .unwrap(),
266
- },
267
- Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls {
268
- listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
269
- .unwrap(),
270
- acceptor: acceptor.clone(),
271
- },
248
+ Listener::Tcp(listener) => TokioListener::Tcp(
249
+ TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap()).unwrap(),
250
+ ),
251
+ Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls(
252
+ TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap()).unwrap(),
253
+ acceptor.clone(),
254
+ ),
255
+ Listener::Unix(listener) => TokioListener::Unix(
256
+ TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap()).unwrap(),
257
+ ),
258
+ Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls(
259
+ TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap()).unwrap(),
260
+ acceptor.clone(),
261
+ ),
272
262
  }
273
263
  }
274
264
  }
@@ -307,6 +297,7 @@ fn connect_tcp_socket(addr: IpAddr, port: u16) -> Result<TcpListener> {
307
297
  socket.set_nonblocking(true).ok();
308
298
  socket.set_nodelay(true).ok();
309
299
  socket.set_recv_buffer_size(262_144).ok();
300
+ info!("Binding to {:?}", socket_address);
310
301
  socket.bind(&socket_address.into())?;
311
302
  socket.listen(1024)?;
312
303
  Ok(socket.into())
@@ -4,7 +4,7 @@ use crate::{
4
4
  io_stream::IoStream,
5
5
  itsi_server::{RequestJob, Server},
6
6
  lifecycle_event::LifecycleEvent,
7
- listener::{Listener, TokioListener},
7
+ listener::{Listener, ListenerInfo},
8
8
  thread_worker::{build_thread_workers, ThreadWorker},
9
9
  },
10
10
  };
@@ -87,18 +87,25 @@ impl SingleMode {
87
87
  pub fn run(self: Arc<Self>) -> Result<()> {
88
88
  let mut listener_task_set = JoinSet::new();
89
89
  let self_ref = Arc::new(self);
90
- self_ref.build_runtime().block_on(async {
90
+ let runtime = self_ref.build_runtime();
91
91
 
92
- for listener in self_ref.listeners.clone().iter() {
93
- let listener = Arc::new(listener.to_tokio_listener());
92
+ runtime.block_on(async {
93
+ let tokio_listeners = self_ref
94
+ .listeners
95
+ .iter()
96
+ .map(|list| Arc::new(list.to_tokio_listener()))
97
+ .collect::<Vec<_>>();
98
+ for listener in tokio_listeners.iter() {
94
99
  let mut lifecycle_rx = self_ref.lifecycle_channel.subscribe();
100
+ let listener_info = Arc::new(listener.listener_info());
95
101
  let self_ref = self_ref.clone();
96
102
  let listener = listener.clone();
97
103
  let (shutdown_sender, mut shutdown_receiver) = tokio::sync::watch::channel::<RunningPhase>(RunningPhase::Running);
98
104
  let listener_clone = listener.clone();
99
105
 
100
- tokio::spawn(async move {
101
- listener_clone.spawn_state_task().await;
106
+ let shutdown_receiver_clone = shutdown_receiver.clone();
107
+ listener_task_set.spawn(async move {
108
+ listener_clone.spawn_state_task(shutdown_receiver_clone).await;
102
109
  });
103
110
 
104
111
  listener_task_set.spawn(async move {
@@ -107,8 +114,11 @@ impl SingleMode {
107
114
  tokio::select! {
108
115
  accept_result = listener.accept() => match accept_result {
109
116
  Ok(accept_result) => {
110
- if let Err(e) = strategy.serve_connection(accept_result, listener.clone(), shutdown_receiver.clone()).await {
111
- error!("Error in serve_connection {:?}", e)
117
+ match strategy.serve_connection(accept_result, listener_info.clone(), shutdown_receiver.clone()).await {
118
+ Ok(_) => {
119
+ debug!("Connection accepted and served");
120
+ },
121
+ Err(e) => error!("Error in serve_connection {:?}", e)
112
122
  }
113
123
  },
114
124
  Err(e) => debug!("Listener.accept failed {:?}", e),
@@ -130,23 +140,23 @@ impl SingleMode {
130
140
  }
131
141
  }
132
142
  }
133
- if let Ok(listener) = Arc::try_unwrap(listener){
134
- listener.unbind();
135
- }
136
143
  });
137
144
 
138
145
  }
139
146
 
140
147
  while let Some(_res) = listener_task_set.join_next().await {}
148
+
141
149
  });
150
+ runtime.shutdown_timeout(Duration::from_millis(100));
142
151
 
152
+ info!("Runtime has shut down");
143
153
  Ok(())
144
154
  }
145
155
 
146
156
  pub(crate) async fn serve_connection(
147
157
  &self,
148
158
  stream: IoStream,
149
- listener: Arc<TokioListener>,
159
+ listener: Arc<ListenerInfo>,
150
160
  shutdown_channel: tokio::sync::watch::Receiver<RunningPhase>,
151
161
  ) -> Result<()> {
152
162
  let sender_clone = self.sender.clone();
@@ -186,7 +186,15 @@ pub fn load_private_key(path: &str) -> PrivateKeyDer<'static> {
186
186
  pub fn generate_ca_signed_cert(
187
187
  domains: Vec<String>,
188
188
  ) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
189
- info!("Generating New Itsi CA - Self signed Certificate. Use `itsi ca export` to export the CA certificate for import into your local trust store.");
189
+ info!(
190
+ domains = format!("{}", domains.join(", ")),
191
+ "Self signed cert",
192
+ );
193
+ info!(
194
+ "Add {} to your system's trusted cert store to resolve certificate errors.",
195
+ format!("{}/itsi_dev_ca.crt", ITSI_LOCAL_CA_DIR.to_str().unwrap())
196
+ );
197
+ info!("Dev CA path can be overridden by setting env var: `ITSI_LOCAL_CA_DIR`.");
190
198
  let (ca_key_pem, ca_cert_pem) = get_or_create_local_dev_ca()?;
191
199
 
192
200
  let ca_kp = KeyPair::from_pem(&ca_key_pem).expect("Failed to load CA key");
@@ -198,10 +206,6 @@ pub fn generate_ca_signed_cert(
198
206
  let ee_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
199
207
  let mut ee_params = CertificateParams::default();
200
208
 
201
- info!(
202
- "Generated certificate will be valid for domains {:?}",
203
- domains
204
- );
205
209
  use std::net::IpAddr;
206
210
 
207
211
  ee_params.subject_alt_names = domains
@@ -0,0 +1,91 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Itsi - Default</title>
6
+ <style>
7
+ * {
8
+ box-sizing: border-box;
9
+ margin: 0;
10
+ padding: 0;
11
+ }
12
+ body {
13
+ font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
14
+ background-color: #f4f4f4;
15
+ color: #333;
16
+ line-height: 1.6;
17
+ }
18
+ .container {
19
+ max-width: 700px;
20
+ margin: 3rem auto;
21
+ background: #fff;
22
+ border-radius: 8px;
23
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
24
+ padding: 2rem;
25
+ }
26
+ h1 {
27
+ font-size: 1.8rem;
28
+ margin-bottom: 1rem;
29
+ text-align: center;
30
+ color: #444;
31
+ }
32
+ p {
33
+ margin-bottom: 1rem;
34
+ text-align: center;
35
+ color: #666;
36
+ }
37
+ ul.fields {
38
+ list-style: none;
39
+ margin-top: 1.5rem;
40
+ padding: 0;
41
+ }
42
+ ul.fields li {
43
+ background: #fafafa;
44
+ border: 1px solid #eee;
45
+ border-radius: 5px;
46
+ padding: 0.75rem;
47
+ margin-bottom: 0.75rem;
48
+ display: flex;
49
+ justify-content: space-between;
50
+ align-items: center;
51
+ }
52
+ .label {
53
+ font-weight: bold;
54
+ margin-right: 1rem;
55
+ }
56
+ </style>
57
+ </head>
58
+ <body>
59
+ <div class="container">
60
+ <h1>You're running on Itsi!</h1>
61
+ <p>RACK environment:</p>
62
+
63
+ <ul class="fields">
64
+ <li>
65
+ <span class="label">REQUEST_METHOD:</span>
66
+ <span>%{REQUEST_METHOD}</span>
67
+ </li>
68
+ <li>
69
+ <span class="label">PATH_INFO:</span>
70
+ <span>%{PATH_INFO}</span>
71
+ </li>
72
+ <li>
73
+ <span class="label">SERVER_NAME:</span>
74
+ <span>%{SERVER_NAME}</span>
75
+ </li>
76
+ <li>
77
+ <span class="label">SERVER_PORT:</span>
78
+ <span>%{SERVER_PORT}</span>
79
+ </li>
80
+ <li>
81
+ <span class="label">REMOTE_ADDR:</span>
82
+ <span>%{REMOTE_ADDR}</span>
83
+ </li>
84
+ <li>
85
+ <span class="label">HTTP_USER_AGENT:</span>
86
+ <span>%{HTTP_USER_AGENT}</span>
87
+ </li>
88
+ </ul>
89
+ </div>
90
+ </body>
91
+ </html>
@@ -1,6 +1,6 @@
1
1
  if defined?(ActiveSupport::IsolatedExecutionState) && !ENV["ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL"]
2
2
  Itsi.log_info \
3
3
  "ActiveSupport Isolated Execution state detected. Automatically switching to :fiber mode. "\
4
- "Use ENV['ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL'] to disable this behavior"
4
+ "Set ITSI_DISABLE_AS_AUTO_FIBER_ISOLATION_LEVEL to disable this behavior"
5
5
  ActiveSupport::IsolatedExecutionState.isolation_level = :fiber
6
6
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Server
5
- VERSION = "0.1.7"
5
+ VERSION = "0.1.8"
6
6
  end
7
7
  end
data/lib/itsi/server.rb CHANGED
@@ -6,6 +6,9 @@ require_relative "signals"
6
6
  require_relative "request"
7
7
  require_relative "stream_io"
8
8
  require_relative "server/rack/handler/itsi"
9
+ require 'erb'
10
+
11
+ DEFAULT_INDEX = IO.read(__dir__ + '/index.html.erb')
9
12
 
10
13
  module Itsi
11
14
  class Server
@@ -14,8 +17,25 @@ module Itsi
14
17
  @running ||= false
15
18
  end
16
19
 
17
- def self.start(app:, **opts)
18
- server = new(app: ->{app}, **opts)
20
+ def self.start(
21
+ app: ->(env){
22
+ [env['CONTENT_TYPE'], env['HTTP_ACCEPT']].include?('application/json') ?
23
+ [200, {"Content-Type" => "application/json"}, ["{\"message\": \"You're running on Itsi!\"}"]] :
24
+ [200, {"Content-Type" => "text/html"}, [
25
+ DEFAULT_INDEX % {
26
+ REQUEST_METHOD: env['REQUEST_METHOD'],
27
+ PATH_INFO: env['PATH_INFO'],
28
+ SERVER_NAME: env['SERVER_NAME'],
29
+ SERVER_PORT: env['SERVER_PORT'],
30
+ REMOTE_ADDR: env['REMOTE_ADDR'],
31
+ HTTP_USER_AGENT: env['HTTP_USER_AGENT']
32
+ }
33
+ ]]
34
+ },
35
+ binds: ['http://0.0.0.0:3000'],
36
+ **opts
37
+ )
38
+ server = new(app: ->{app}, binds: binds, **opts)
19
39
  @running = true
20
40
  Signal.trap('INT', 'DEFAULT')
21
41
  server.start
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: itsi-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters
@@ -49,6 +49,8 @@ files:
49
49
  - ".rubocop.yml"
50
50
  - CHANGELOG.md
51
51
  - CODE_OF_CONDUCT.md
52
+ - Cargo.lock
53
+ - Cargo.toml
52
54
  - LICENSE.txt
53
55
  - README.md
54
56
  - Rakefile
@@ -70,6 +72,7 @@ files:
70
72
  - ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs
71
73
  - ext/itsi_scheduler/src/itsi_scheduler/timer.rs
72
74
  - ext/itsi_scheduler/src/lib.rs
75
+ - ext/itsi_server/Cargo.lock
73
76
  - ext/itsi_server/Cargo.toml
74
77
  - ext/itsi_server/extconf.rb
75
78
  - ext/itsi_server/src/body_proxy/big_bytes.rs
@@ -99,6 +102,7 @@ files:
99
102
  - ext/itsi_tracing/Cargo.lock
100
103
  - ext/itsi_tracing/Cargo.toml
101
104
  - ext/itsi_tracing/src/lib.rs
105
+ - lib/itsi/index.html.erb
102
106
  - lib/itsi/request.rb
103
107
  - lib/itsi/server.rb
104
108
  - lib/itsi/server/rack/handler/itsi.rb