itsi-server 0.1.1 → 0.1.2

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/exe/itsi +88 -28
  3. data/ext/itsi_error/Cargo.toml +2 -0
  4. data/ext/itsi_error/src/from.rs +70 -0
  5. data/ext/itsi_error/src/lib.rs +10 -37
  6. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  7. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  8. data/ext/itsi_rb_helpers/Cargo.toml +2 -0
  9. data/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
  10. data/ext/itsi_rb_helpers/src/lib.rs +90 -10
  11. data/ext/itsi_scheduler/Cargo.toml +24 -0
  12. data/ext/itsi_scheduler/extconf.rb +6 -0
  13. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  14. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  15. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  16. data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
  17. data/ext/itsi_scheduler/src/lib.rs +38 -0
  18. data/ext/itsi_server/Cargo.toml +14 -2
  19. data/ext/itsi_server/extconf.rb +1 -1
  20. data/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
  21. data/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
  22. data/ext/itsi_server/src/body_proxy/mod.rs +2 -0
  23. data/ext/itsi_server/src/lib.rs +58 -7
  24. data/ext/itsi_server/src/request/itsi_request.rs +238 -104
  25. data/ext/itsi_server/src/response/itsi_response.rs +347 -0
  26. data/ext/itsi_server/src/response/mod.rs +1 -0
  27. data/ext/itsi_server/src/server/bind.rs +50 -20
  28. data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
  29. data/ext/itsi_server/src/server/io_stream.rs +104 -0
  30. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +11 -30
  31. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +3 -50
  32. data/ext/itsi_server/src/server/itsi_server.rs +181 -133
  33. data/ext/itsi_server/src/server/lifecycle_event.rs +8 -0
  34. data/ext/itsi_server/src/server/listener.rs +169 -128
  35. data/ext/itsi_server/src/server/mod.rs +7 -1
  36. data/ext/itsi_server/src/server/process_worker.rs +196 -0
  37. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +253 -0
  38. data/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
  39. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +238 -0
  40. data/ext/itsi_server/src/server/signal.rs +57 -0
  41. data/ext/itsi_server/src/server/thread_worker.rs +368 -0
  42. data/ext/itsi_server/src/server/tls.rs +42 -28
  43. data/ext/itsi_tracing/Cargo.toml +4 -0
  44. data/ext/itsi_tracing/src/lib.rs +36 -6
  45. data/lib/itsi/request.rb +30 -14
  46. data/lib/itsi/server/rack/handler/itsi.rb +25 -0
  47. data/lib/itsi/server/scheduler_mode.rb +6 -0
  48. data/lib/itsi/server/version.rb +1 -1
  49. data/lib/itsi/server.rb +68 -2
  50. data/lib/itsi/signals.rb +18 -0
  51. data/lib/itsi/stream_io.rb +38 -0
  52. metadata +41 -14
  53. data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
  54. data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
@@ -0,0 +1,122 @@
1
+ use super::big_bytes::BigBytes;
2
+ use bytes::Bytes;
3
+ use futures::executor::block_on;
4
+ use http_body_util::{BodyDataStream, BodyExt};
5
+ use hyper::body::Incoming;
6
+ use magnus::{error::Result as MagnusResult, scan_args, IntoValue, RString, Ruby, Value};
7
+ use parking_lot::Mutex;
8
+ use std::sync::{
9
+ atomic::{self, AtomicBool},
10
+ Arc,
11
+ };
12
+ use tokio_stream::StreamExt;
13
+
14
+ #[magnus::wrap(class = "Itsi::BodyProxy", free_immediately, size)]
15
+ #[derive(Debug, Clone)]
16
+ pub struct ItsiBodyProxy {
17
+ pub incoming: Arc<Mutex<BodyDataStream<Incoming>>>,
18
+ pub closed: Arc<AtomicBool>,
19
+ pub buf: Arc<Mutex<Vec<u8>>>,
20
+ }
21
+
22
+ pub enum ItsiBody {
23
+ Buffered(BigBytes),
24
+ Stream(ItsiBodyProxy),
25
+ }
26
+
27
+ impl ItsiBody {
28
+ pub fn into_value(&self) -> Value {
29
+ match self {
30
+ ItsiBody::Buffered(bytes) => bytes.as_value(),
31
+ ItsiBody::Stream(proxy) => proxy.clone().into_value(),
32
+ }
33
+ }
34
+ }
35
+ impl ItsiBodyProxy {
36
+ pub fn new(incoming: Incoming) -> Self {
37
+ ItsiBodyProxy {
38
+ incoming: Arc::new(Mutex::new(incoming.into_data_stream())),
39
+ closed: Arc::new(AtomicBool::new(false)),
40
+ buf: Arc::new(Mutex::new(vec![])),
41
+ }
42
+ }
43
+ /// Read up to the next line-break OR EOF
44
+ pub fn gets(&self) -> MagnusResult<Option<Bytes>> {
45
+ self.verify_open()?;
46
+ let mut stream = self.incoming.lock();
47
+ let mut buf = self.buf.lock();
48
+ while !buf.contains(&b'\n') {
49
+ if let Some(chunk) = block_on(stream.next()) {
50
+ let chunk = chunk.map_err(|err| {
51
+ magnus::Error::new(
52
+ magnus::exception::exception(),
53
+ format!("Error reading body {:?}", err),
54
+ )
55
+ })?;
56
+ buf.extend_from_slice(&chunk);
57
+ } else {
58
+ break;
59
+ }
60
+ }
61
+ if let Some(pos) = buf.iter().position(|&x| x == b'\n') {
62
+ let line = buf.drain(..=pos).collect::<Vec<u8>>();
63
+ Ok(Some(line.into()))
64
+ } else if !buf.is_empty() {
65
+ let line = buf.drain(..).collect::<Vec<u8>>();
66
+ Ok(Some(line.into()))
67
+ } else {
68
+ Ok(None)
69
+ }
70
+ }
71
+
72
+ pub fn read(&self, args: &[Value]) -> MagnusResult<Option<RString>> {
73
+ self.verify_open()?;
74
+ let scanned =
75
+ scan_args::scan_args::<(), (Option<usize>, Option<RString>), (), (), (), ()>(args)?;
76
+ let (length, mut buffer) = scanned.optional;
77
+ let mut stream = self.incoming.lock();
78
+ let mut buf = self.buf.lock();
79
+
80
+ while length.is_none_or(|target_length| buf.len() < target_length) {
81
+ if let Some(chunk) = block_on(stream.next()) {
82
+ let chunk = chunk.map_err(|err| {
83
+ magnus::Error::new(
84
+ magnus::exception::exception(),
85
+ format!("Error reading body {:?}", err),
86
+ )
87
+ })?;
88
+ buf.extend_from_slice(&chunk);
89
+ } else if length.is_some() {
90
+ return Ok(None);
91
+ } else {
92
+ break;
93
+ }
94
+ }
95
+ let output_string = buffer.take().unwrap_or(RString::buf_new(buf.len()));
96
+ output_string.cat(buf.clone());
97
+ buf.clear();
98
+ Ok(Some(output_string))
99
+ }
100
+
101
+ /// Equivalent to calling gets and yielding it, until we reach EOF
102
+ pub fn each(ruby: &Ruby, rbself: &Self) -> MagnusResult<()> {
103
+ let proc = ruby.block_proc()?;
104
+ while let Some(str) = rbself.gets()? {
105
+ proc.call::<_, Value>((str,))?;
106
+ }
107
+ Ok(())
108
+ }
109
+
110
+ fn verify_open(&self) -> MagnusResult<()> {
111
+ if self.closed.load(atomic::Ordering::SeqCst) {
112
+ return Err(magnus::Error::new(
113
+ magnus::exception::exception(),
114
+ "Body stream is closed",
115
+ ));
116
+ }
117
+ Ok(())
118
+ }
119
+ pub fn close(&self) {
120
+ self.closed.store(true, atomic::Ordering::SeqCst);
121
+ }
122
+ }
@@ -0,0 +1,2 @@
1
+ pub mod big_bytes;
2
+ pub mod itsi_body_proxy;
@@ -1,11 +1,14 @@
1
+ use body_proxy::itsi_body_proxy::ItsiBodyProxy;
1
2
  use magnus::{error::Result, function, method, value::Lazy, Module, Object, RClass, RModule, Ruby};
2
3
  use request::itsi_request::ItsiRequest;
3
- use server::itsi_server::Server;
4
- use stream_writer::StreamWriter;
4
+ use response::itsi_response::ItsiResponse;
5
+ use server::{itsi_server::Server, signal::reset_signal_handlers};
6
+ use tracing::*;
5
7
 
8
+ pub mod body_proxy;
6
9
  pub mod request;
10
+ pub mod response;
7
11
  pub mod server;
8
- pub mod stream_writer;
9
12
 
10
13
  pub static ITSI_MODULE: Lazy<RModule> = Lazy::new(|ruby| ruby.define_module("Itsi").unwrap());
11
14
  pub static ITSI_SERVER: Lazy<RClass> = Lazy::new(|ruby| {
@@ -18,18 +21,51 @@ pub static ITSI_REQUEST: Lazy<RClass> = Lazy::new(|ruby| {
18
21
  .define_class("Request", ruby.class_object())
19
22
  .unwrap()
20
23
  });
21
- pub static ITSI_STREAM_WRITER: Lazy<RClass> = Lazy::new(|ruby| {
24
+
25
+ pub static ITSI_RESPONSE: Lazy<RClass> = Lazy::new(|ruby| {
26
+ ruby.get_inner(&ITSI_MODULE)
27
+ .define_class("Response", ruby.class_object())
28
+ .unwrap()
29
+ });
30
+
31
+ pub static ITSI_BODY_PROXY: Lazy<RClass> = Lazy::new(|ruby| {
32
+ ruby.get_inner(&ITSI_MODULE)
33
+ .define_class("BodyProxy", ruby.class_object())
34
+ .unwrap()
35
+ });
36
+
37
+ pub static ITSI_SERVER_SCHEDULER_TASK: Lazy<RClass> = Lazy::new(|ruby| {
22
38
  ruby.get_inner(&ITSI_MODULE)
23
- .define_class("StreamWriter", ruby.class_object())
39
+ .define_class("ServerSchedulerTask", ruby.class_object())
24
40
  .unwrap()
25
41
  });
26
42
 
43
+ pub fn log_debug(msg: String) {
44
+ debug!(msg);
45
+ }
46
+ pub fn log_info(msg: String) {
47
+ info!(msg);
48
+ }
49
+ pub fn log_warn(msg: String) {
50
+ warn!(msg);
51
+ }
52
+ pub fn log_error(msg: String) {
53
+ error!(msg);
54
+ }
55
+
27
56
  #[magnus::init]
28
57
  fn init(ruby: &Ruby) -> Result<()> {
29
58
  itsi_tracing::init();
30
59
 
60
+ let itsi = ruby.get_inner(&ITSI_MODULE);
61
+ itsi.define_singleton_method("log_debug", function!(log_debug, 1))?;
62
+ itsi.define_singleton_method("log_info", function!(log_info, 1))?;
63
+ itsi.define_singleton_method("log_warn", function!(log_warn, 1))?;
64
+ itsi.define_singleton_method("log_error", function!(log_error, 1))?;
65
+
31
66
  let server = ruby.get_inner(&ITSI_SERVER);
32
67
  server.define_singleton_method("new", function!(Server::new, -1))?;
68
+ server.define_singleton_method("reset_signal_handlers", function!(reset_signal_handlers, 0))?;
33
69
  server.define_method("start", method!(Server::start, 0))?;
34
70
 
35
71
  let request = ruby.get_inner(&ITSI_REQUEST);
@@ -41,12 +77,27 @@ fn init(ruby: &Ruby) -> Result<()> {
41
77
  request.define_method("rack_protocol", method!(ItsiRequest::rack_protocol, 0))?;
42
78
  request.define_method("host", method!(ItsiRequest::host, 0))?;
43
79
  request.define_method("headers", method!(ItsiRequest::headers, 0))?;
80
+ request.define_method("scheme", method!(ItsiRequest::scheme, 0))?;
44
81
  request.define_method("remote_addr", method!(ItsiRequest::remote_addr, 0))?;
45
82
  request.define_method("port", method!(ItsiRequest::port, 0))?;
46
83
  request.define_method("body", method!(ItsiRequest::body, 0))?;
84
+ request.define_method("response", method!(ItsiRequest::response, 0))?;
85
+
86
+ let body_proxy = ruby.get_inner(&ITSI_BODY_PROXY);
87
+ body_proxy.define_method("gets", method!(ItsiBodyProxy::gets, 0))?;
88
+ body_proxy.define_method("each", method!(ItsiBodyProxy::each, 0))?;
89
+ body_proxy.define_method("read", method!(ItsiBodyProxy::read, -1))?;
90
+ body_proxy.define_method("close", method!(ItsiBodyProxy::close, 0))?;
47
91
 
48
- let stream_writer = ruby.get_inner(&ITSI_STREAM_WRITER);
49
- stream_writer.define_method("write", method!(StreamWriter::write, 1))?;
92
+ let response = ruby.get_inner(&ITSI_RESPONSE);
93
+ response.define_method("add_header", method!(ItsiResponse::add_header, 2))?;
94
+ response.define_method("status=", method!(ItsiResponse::set_status, 1))?;
95
+ response.define_method("send_frame", method!(ItsiResponse::send_frame, 1))?;
96
+ response.define_method("send_and_close", method!(ItsiResponse::send_and_close, 1))?;
97
+ response.define_method("close_write", method!(ItsiResponse::close_write, 0))?;
98
+ response.define_method("close_read", method!(ItsiResponse::close_read, 0))?;
99
+ response.define_method("close", method!(ItsiResponse::close, 0))?;
100
+ response.define_method("hijack", method!(ItsiResponse::hijack, 1))?;
50
101
 
51
102
  Ok(())
52
103
  }
@@ -1,143 +1,277 @@
1
- use std::{collections::HashMap, sync::Arc};
2
-
3
- use crate::server::listener::{Listener, SockAddr};
1
+ use crate::{
2
+ body_proxy::{
3
+ big_bytes::BigBytes,
4
+ itsi_body_proxy::{ItsiBody, ItsiBodyProxy},
5
+ },
6
+ response::itsi_response::ItsiResponse,
7
+ server::{
8
+ itsi_server::{RequestJob, Server},
9
+ listener::{SockAddr, TokioListener},
10
+ serve_strategy::single_mode::RunningPhase,
11
+ },
12
+ };
4
13
  use bytes::Bytes;
5
- use http::request::Parts;
6
- use http_body_util::BodyExt;
14
+ use derive_more::Debug;
15
+ use futures::StreamExt;
16
+ use http::{request::Parts, Response, StatusCode};
17
+ use http_body_util::{combinators::BoxBody, BodyExt, Empty};
7
18
  use hyper::{body::Incoming, Request};
8
- use magnus::error::Result;
19
+ use itsi_error::from::CLIENT_CONNECTION_CLOSED;
20
+ use itsi_tracing::{debug, error};
21
+ use magnus::{
22
+ error::{ErrorType, Result as MagnusResult},
23
+ Error,
24
+ };
25
+ use magnus::{
26
+ value::{LazyId, Opaque, ReprValue},
27
+ RClass, Ruby, Value,
28
+ };
29
+ use std::{convert::Infallible, fmt, io::Write, sync::Arc, time::Instant};
30
+ use tokio::sync::{
31
+ mpsc::{self},
32
+ watch,
33
+ };
34
+ static ID_CALL: LazyId = LazyId::new("call");
35
+ static ID_MESSAGE: LazyId = LazyId::new("message");
36
+ static ID_BACKTRACE: LazyId = LazyId::new("backtrace");
9
37
 
10
- #[magnus::wrap(class = "Itsi::Request", free_immediately, size)]
11
38
  #[derive(Debug)]
39
+ #[magnus::wrap(class = "Itsi::Request", free_immediately, size)]
12
40
  pub struct ItsiRequest {
13
- pub path: String,
14
- pub script_name: String,
15
- pub query_string: String,
16
- pub method: String,
17
- pub version: String,
18
- pub rack_protocol: Vec<String>,
19
- pub host: String,
20
- pub scheme: String,
21
- pub headers: HashMap<String, String>,
22
- pub remote_addr: String,
23
- pub port: u16,
24
- pub body: Bytes,
25
41
  pub parts: Parts,
42
+ #[debug(skip)]
43
+ pub body: ItsiBody,
44
+ pub remote_addr: String,
45
+ pub version: String,
46
+ #[debug(skip)]
47
+ pub(crate) listener: Arc<TokioListener>,
48
+ #[debug(skip)]
49
+ pub server: Arc<Server>,
50
+ pub response: ItsiResponse,
51
+ pub start: Instant,
52
+ }
53
+
54
+ impl fmt::Display for ItsiRequest {
55
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56
+ write!(
57
+ f,
58
+ "{} {} {}",
59
+ self.version().unwrap(),
60
+ self.method().unwrap(),
61
+ self.path().unwrap()
62
+ )
63
+ }
26
64
  }
27
65
 
28
66
  impl ItsiRequest {
29
- pub(crate) async fn build_from(
30
- request: Request<Incoming>,
31
- sock_addr: SockAddr,
32
- script_name: String,
33
- listener: Arc<Listener>,
34
- ) -> Self {
35
- let (parts, body) = request.into_parts();
36
- let method = parts.method.to_string();
37
- let port = parts.uri.port_u16().unwrap_or(listener.port());
38
- let query_string = parts.uri.query().unwrap_or("").to_string();
39
- let rack_protocol = parts
40
- .headers
41
- .get("upgrade")
42
- .or_else(|| parts.headers.get("protocol"))
43
- .map(|value| {
44
- value
45
- .to_str()
46
- .unwrap_or("")
47
- .split(',')
48
- .map(|s| s.trim().to_owned())
49
- .collect::<Vec<String>>()
50
- })
51
- .unwrap_or_else(|| vec!["http".to_string()]);
67
+ pub fn is_connection_closed_err(ruby: &Ruby, err: &Error) -> bool {
68
+ match err.error_type() {
69
+ ErrorType::Jump(_) => false,
70
+ ErrorType::Error(_, _) => false,
71
+ ErrorType::Exception(exception) => {
72
+ exception.is_kind_of(ruby.exception_eof_error())
73
+ && err
74
+ .value()
75
+ .map(|v| {
76
+ v.funcall::<_, _, String>(*ID_MESSAGE, ())
77
+ .unwrap_or("".to_string())
78
+ .eq(CLIENT_CONNECTION_CLOSED)
79
+ })
80
+ .unwrap_or(false)
81
+ }
82
+ }
83
+ }
52
84
 
53
- let host = parts
54
- .uri
55
- .host()
56
- .map(ToOwned::to_owned)
57
- .unwrap_or_else(|| listener.host());
85
+ pub fn process(
86
+ self,
87
+ ruby: &Ruby,
88
+ server: RClass,
89
+ app: Opaque<Value>,
90
+ ) -> magnus::error::Result<()> {
91
+ let req = format!("{}", self);
92
+ let response = self.response.clone();
93
+ let start = self.start;
94
+ debug!("{} Started", req);
95
+ let result = server.funcall::<_, _, Value>(*ID_CALL, (app, self));
96
+ if let Err(err) = result {
97
+ Self::internal_error(ruby, response, err);
98
+ }
99
+ debug!("{} Finished in {:?}", req, start.elapsed());
58
100
 
59
- let scheme = parts
60
- .uri
61
- .scheme()
62
- .map(|s| s.to_string())
63
- .unwrap_or_else(|| listener.scheme());
101
+ Ok(())
102
+ }
64
103
 
65
- let headers = parts
66
- .headers
67
- .iter()
68
- .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
69
- .collect();
104
+ pub fn internal_error(ruby: &Ruby, response: ItsiResponse, err: Error) {
105
+ if Self::is_connection_closed_err(ruby, &err) {
106
+ debug!("Connection closed by client");
107
+ response.close();
108
+ } else if let Some(rb_err) = err.value() {
109
+ let backtrace = rb_err
110
+ .funcall::<_, _, Vec<String>>(*ID_BACKTRACE, ())
111
+ .unwrap_or_default();
70
112
 
71
- let path = parts
113
+ error!("Error occurred in Handler: {:?}", rb_err);
114
+ for line in backtrace {
115
+ error!("{}", line);
116
+ }
117
+ response.internal_server_error(err.to_string());
118
+ } else {
119
+ response.internal_server_error(err.to_string());
120
+ }
121
+ }
122
+
123
+ pub fn error(self, message: String) {
124
+ self.response.internal_server_error(message);
125
+ }
126
+
127
+ pub(crate) async fn process_request(
128
+ hyper_request: Request<Incoming>,
129
+ sender: async_channel::Sender<RequestJob>,
130
+ server: Arc<Server>,
131
+ listener: Arc<TokioListener>,
132
+ addr: SockAddr,
133
+ shutdown_rx: watch::Receiver<RunningPhase>,
134
+ ) -> itsi_error::Result<Response<BoxBody<Bytes, Infallible>>> {
135
+ let (request, mut receiver) = ItsiRequest::new(hyper_request, addr, server, listener).await;
136
+
137
+ let response = request.response.clone();
138
+ match sender.send(RequestJob::ProcessRequest(request)).await {
139
+ Err(err) => {
140
+ error!("Error occurred: {}", err);
141
+ let mut response = Response::new(BoxBody::new(Empty::new()));
142
+ *response.status_mut() = StatusCode::BAD_REQUEST;
143
+ Ok(response)
144
+ }
145
+ _ => match receiver.recv().await {
146
+ Some(first_frame) => Ok(response.build(first_frame, receiver, shutdown_rx).await),
147
+ None => Ok(response.build(None, receiver, shutdown_rx).await),
148
+ },
149
+ }
150
+ }
151
+
152
+ pub(crate) async fn new(
153
+ request: Request<Incoming>,
154
+ sock_addr: SockAddr,
155
+ server: Arc<Server>,
156
+ listener: Arc<TokioListener>,
157
+ ) -> (ItsiRequest, mpsc::Receiver<Option<Bytes>>) {
158
+ let (parts, body) = request.into_parts();
159
+ let body = if server.stream_body.is_some_and(|f| f) {
160
+ ItsiBody::Stream(ItsiBodyProxy::new(body))
161
+ } else {
162
+ let mut body_bytes = BigBytes::new();
163
+ let mut stream = body.into_data_stream();
164
+ while let Some(chunk) = stream.next().await {
165
+ let byte_array = chunk.unwrap().to_vec();
166
+ body_bytes.write_all(&byte_array).unwrap();
167
+ }
168
+ ItsiBody::Buffered(body_bytes)
169
+ };
170
+ let response_channel = mpsc::channel::<Option<Bytes>>(100);
171
+ (
172
+ Self {
173
+ remote_addr: sock_addr.to_string(),
174
+ body,
175
+ server,
176
+ listener,
177
+ version: format!("{:?}", &parts.version),
178
+ response: ItsiResponse::new(parts.clone(), response_channel.0),
179
+ start: Instant::now(),
180
+ parts,
181
+ },
182
+ response_channel.1,
183
+ )
184
+ }
185
+
186
+ pub(crate) fn path(&self) -> MagnusResult<&str> {
187
+ Ok(self
188
+ .parts
72
189
  .uri
73
190
  .path()
74
- .strip_prefix(&script_name)
75
- .unwrap_or(parts.uri.path())
76
- .to_string();
77
-
78
- let version = format!("{:?}", parts.version);
79
- let body = body.collect().await.unwrap().to_bytes();
80
-
81
- Self {
82
- remote_addr: sock_addr.to_string(),
83
- body,
84
- parts,
85
- script_name,
86
- query_string,
87
- method,
88
- headers,
89
- path,
90
- version,
91
- rack_protocol,
92
- host,
93
- scheme,
94
- port,
95
- }
191
+ .strip_prefix(&self.server.script_name)
192
+ .unwrap_or(self.parts.uri.path()))
96
193
  }
97
- }
98
194
 
99
- impl ItsiRequest {
100
- pub(crate) fn path(&self) -> Result<String> {
101
- Ok(self.path.clone())
195
+ pub(crate) fn script_name(&self) -> MagnusResult<&str> {
196
+ Ok(&self.server.script_name)
197
+ }
198
+
199
+ pub(crate) fn query_string(&self) -> MagnusResult<&str> {
200
+ Ok(self.parts.uri.query().unwrap_or(""))
102
201
  }
103
202
 
104
- pub(crate) fn script_name(&self) -> Result<String> {
105
- Ok(self.script_name.clone())
203
+ pub(crate) fn method(&self) -> MagnusResult<&str> {
204
+ Ok(self.parts.method.as_str())
106
205
  }
107
206
 
108
- pub(crate) fn query_string(&self) -> Result<String> {
109
- Ok(self.query_string.clone())
207
+ pub(crate) fn version(&self) -> MagnusResult<&str> {
208
+ Ok(&self.version)
110
209
  }
111
210
 
112
- pub(crate) fn method(&self) -> Result<String> {
113
- Ok(self.method.clone())
211
+ pub(crate) fn rack_protocol(&self) -> MagnusResult<Vec<&str>> {
212
+ Ok(self
213
+ .parts
214
+ .headers
215
+ .get("upgrade")
216
+ .or_else(|| self.parts.headers.get("protocol"))
217
+ .map(|value| {
218
+ value
219
+ .to_str()
220
+ .unwrap_or("")
221
+ .split(',')
222
+ .map(|s| s.trim())
223
+ .collect::<Vec<&str>>()
224
+ })
225
+ .unwrap_or_else(|| vec!["http"]))
114
226
  }
115
227
 
116
- pub(crate) fn version(&self) -> Result<String> {
117
- Ok(self.version.clone())
228
+ pub(crate) fn host(&self) -> MagnusResult<String> {
229
+ Ok(self
230
+ .parts
231
+ .uri
232
+ .host()
233
+ .map(|host| host.to_string())
234
+ .unwrap_or_else(|| self.listener.host()))
118
235
  }
119
236
 
120
- pub(crate) fn rack_protocol(&self) -> Result<Vec<String>> {
121
- Ok(self.rack_protocol.clone())
237
+ pub(crate) fn scheme(&self) -> MagnusResult<String> {
238
+ Ok(self
239
+ .parts
240
+ .uri
241
+ .scheme()
242
+ .map(|scheme| scheme.to_string())
243
+ .unwrap_or_else(|| self.listener.scheme()))
122
244
  }
123
245
 
124
- pub(crate) fn host(&self) -> Result<String> {
125
- Ok(self.host.clone())
246
+ pub(crate) fn headers(&self) -> MagnusResult<Vec<(String, &str)>> {
247
+ Ok(self
248
+ .parts
249
+ .headers
250
+ .iter()
251
+ .map(|(hn, hv)| {
252
+ let key = match hn.as_str() {
253
+ "content-length" => "CONTENT_LENGTH".to_string(),
254
+ "content-type" => "CONTENT_TYPE".to_string(),
255
+ _ => format!("HTTP_{}", hn.as_str().to_uppercase().replace("-", "_")),
256
+ };
257
+ (key, hv.to_str().unwrap_or(""))
258
+ })
259
+ .collect())
126
260
  }
127
261
 
128
- pub(crate) fn headers(&self) -> Result<HashMap<String, String>> {
129
- Ok(self.headers.clone())
262
+ pub(crate) fn remote_addr(&self) -> MagnusResult<&str> {
263
+ Ok(&self.remote_addr)
130
264
  }
131
265
 
132
- pub(crate) fn remote_addr(&self) -> Result<String> {
133
- Ok(self.remote_addr.clone())
266
+ pub(crate) fn port(&self) -> MagnusResult<u16> {
267
+ Ok(self.parts.uri.port_u16().unwrap_or(self.listener.port()))
134
268
  }
135
269
 
136
- pub(crate) fn port(&self) -> Result<u16> {
137
- Ok(self.port)
270
+ pub(crate) fn body(&self) -> MagnusResult<Value> {
271
+ Ok(self.body.into_value())
138
272
  }
139
273
 
140
- pub(crate) fn body(&self) -> Result<Bytes> {
141
- Ok(self.body.clone())
274
+ pub(crate) fn response(&self) -> MagnusResult<ItsiResponse> {
275
+ Ok(self.response.clone())
142
276
  }
143
277
  }