itsi-scheduler 0.2.22-aarch64-linux
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 +7 -0
- data/.rubocop.yml +8 -0
- data/Cargo.lock +997 -0
- data/Cargo.toml +7 -0
- data/Rakefile +39 -0
- data/ext/itsi_acme/Cargo.toml +86 -0
- data/ext/itsi_acme/examples/high_level.rs +63 -0
- data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
- data/ext/itsi_acme/examples/low_level.rs +87 -0
- data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
- data/ext/itsi_acme/src/acceptor.rs +81 -0
- data/ext/itsi_acme/src/acme.rs +354 -0
- data/ext/itsi_acme/src/axum.rs +86 -0
- data/ext/itsi_acme/src/cache.rs +39 -0
- data/ext/itsi_acme/src/caches/boxed.rs +80 -0
- data/ext/itsi_acme/src/caches/composite.rs +69 -0
- data/ext/itsi_acme/src/caches/dir.rs +106 -0
- data/ext/itsi_acme/src/caches/mod.rs +11 -0
- data/ext/itsi_acme/src/caches/no.rs +78 -0
- data/ext/itsi_acme/src/caches/test.rs +136 -0
- data/ext/itsi_acme/src/config.rs +172 -0
- data/ext/itsi_acme/src/https_helper.rs +69 -0
- data/ext/itsi_acme/src/incoming.rs +142 -0
- data/ext/itsi_acme/src/jose.rs +161 -0
- data/ext/itsi_acme/src/lib.rs +142 -0
- data/ext/itsi_acme/src/resolver.rs +59 -0
- data/ext/itsi_acme/src/state.rs +424 -0
- data/ext/itsi_error/Cargo.lock +368 -0
- data/ext/itsi_error/Cargo.toml +12 -0
- data/ext/itsi_error/src/lib.rs +140 -0
- data/ext/itsi_instrument_entry/Cargo.toml +15 -0
- data/ext/itsi_instrument_entry/src/lib.rs +31 -0
- data/ext/itsi_rb_helpers/Cargo.lock +355 -0
- data/ext/itsi_rb_helpers/Cargo.toml +11 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
- data/ext/itsi_rb_helpers/src/lib.rs +232 -0
- data/ext/itsi_scheduler/Cargo.toml +24 -0
- data/ext/itsi_scheduler/extconf.rb +11 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler.rs +320 -0
- data/ext/itsi_scheduler/src/lib.rs +39 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +94 -0
- data/ext/itsi_server/src/default_responses/mod.rs +14 -0
- data/ext/itsi_server/src/env.rs +43 -0
- data/ext/itsi_server/src/lib.rs +154 -0
- data/ext/itsi_server/src/prelude.rs +2 -0
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +116 -0
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +149 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +346 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +265 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +399 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +447 -0
- data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +545 -0
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +650 -0
- data/ext/itsi_server/src/ruby_types/itsi_server.rs +102 -0
- data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
- data/ext/itsi_server/src/server/binds/bind.rs +204 -0
- data/ext/itsi_server/src/server/binds/bind_protocol.rs +37 -0
- data/ext/itsi_server/src/server/binds/listener.rs +485 -0
- data/ext/itsi_server/src/server/binds/mod.rs +4 -0
- data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +132 -0
- data/ext/itsi_server/src/server/binds/tls.rs +278 -0
- data/ext/itsi_server/src/server/byte_frame.rs +32 -0
- data/ext/itsi_server/src/server/frame_stream.rs +143 -0
- data/ext/itsi_server/src/server/http_message_types.rs +230 -0
- data/ext/itsi_server/src/server/io_stream.rs +128 -0
- data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
- data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +93 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +329 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +300 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +188 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +168 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +183 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +209 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +133 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +122 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +407 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +155 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +54 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +138 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +269 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +62 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +218 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
- data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
- data/ext/itsi_server/src/server/mod.rs +14 -0
- data/ext/itsi_server/src/server/process_worker.rs +247 -0
- data/ext/itsi_server/src/server/redirect_type.rs +26 -0
- data/ext/itsi_server/src/server/request_job.rs +11 -0
- data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +100 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +411 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +31 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +449 -0
- data/ext/itsi_server/src/server/signal.rs +129 -0
- data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
- data/ext/itsi_server/src/server/thread_worker.rs +504 -0
- data/ext/itsi_server/src/services/cache_store.rs +74 -0
- data/ext/itsi_server/src/services/itsi_http_service.rs +270 -0
- data/ext/itsi_server/src/services/mime_types.rs +2896 -0
- data/ext/itsi_server/src/services/mod.rs +6 -0
- data/ext/itsi_server/src/services/password_hasher.rs +89 -0
- data/ext/itsi_server/src/services/rate_limiter.rs +609 -0
- data/ext/itsi_server/src/services/static_file_server.rs +1400 -0
- data/ext/itsi_tracing/Cargo.lock +274 -0
- data/ext/itsi_tracing/Cargo.toml +17 -0
- data/ext/itsi_tracing/src/lib.rs +370 -0
- data/itsi-scheduler-100.png +0 -0
- data/lib/itsi/schedule_refinement.rb +96 -0
- data/lib/itsi/scheduler/3.1/itsi_scheduler.so +0 -0
- data/lib/itsi/scheduler/3.2/itsi_scheduler.so +0 -0
- data/lib/itsi/scheduler/3.3/itsi_scheduler.so +0 -0
- data/lib/itsi/scheduler/3.4/itsi_scheduler.so +0 -0
- data/lib/itsi/scheduler/4.0/itsi_scheduler.so +0 -0
- data/lib/itsi/scheduler/native_extension.rb +34 -0
- data/lib/itsi/scheduler/version.rb +7 -0
- data/lib/itsi/scheduler.rb +153 -0
- data/vendor/rb-sys-build/.cargo-ok +1 -0
- data/vendor/rb-sys-build/.cargo_vcs_info.json +6 -0
- data/vendor/rb-sys-build/Cargo.lock +294 -0
- data/vendor/rb-sys-build/Cargo.toml +71 -0
- data/vendor/rb-sys-build/Cargo.toml.orig +32 -0
- data/vendor/rb-sys-build/LICENSE-APACHE +190 -0
- data/vendor/rb-sys-build/LICENSE-MIT +21 -0
- data/vendor/rb-sys-build/src/bindings/sanitizer.rs +185 -0
- data/vendor/rb-sys-build/src/bindings/stable_api.rs +247 -0
- data/vendor/rb-sys-build/src/bindings/wrapper.h +71 -0
- data/vendor/rb-sys-build/src/bindings.rs +280 -0
- data/vendor/rb-sys-build/src/cc.rs +421 -0
- data/vendor/rb-sys-build/src/lib.rs +12 -0
- data/vendor/rb-sys-build/src/rb_config/flags.rs +101 -0
- data/vendor/rb-sys-build/src/rb_config/library.rs +132 -0
- data/vendor/rb-sys-build/src/rb_config/search_path.rs +57 -0
- data/vendor/rb-sys-build/src/rb_config.rs +906 -0
- data/vendor/rb-sys-build/src/utils.rs +53 -0
- metadata +210 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
use crate::server::{
|
|
2
|
+
frame_stream::{BufferedStream, FrameStream},
|
|
3
|
+
http_message_types::{HttpBody, HttpResponse},
|
|
4
|
+
serve_strategy::single_mode::RunningPhase,
|
|
5
|
+
};
|
|
6
|
+
use bytes::{Buf, Bytes};
|
|
7
|
+
use derive_more::Debug;
|
|
8
|
+
use futures::stream::{unfold, StreamExt};
|
|
9
|
+
use http::{
|
|
10
|
+
header::{ACCEPT, TRANSFER_ENCODING},
|
|
11
|
+
request::Parts,
|
|
12
|
+
HeaderMap, HeaderName, HeaderValue, Request, Response, StatusCode,
|
|
13
|
+
};
|
|
14
|
+
use http_body_util::Empty;
|
|
15
|
+
use hyper::upgrade::Upgraded;
|
|
16
|
+
use hyper_util::rt::TokioIo;
|
|
17
|
+
use itsi_error::Result;
|
|
18
|
+
use itsi_rb_helpers::call_without_gvl;
|
|
19
|
+
use itsi_tracing::error;
|
|
20
|
+
use magnus::error::Result as MagnusResult;
|
|
21
|
+
use memchr::{memchr, memchr_iter};
|
|
22
|
+
use parking_lot::RwLock;
|
|
23
|
+
use std::{
|
|
24
|
+
collections::HashMap,
|
|
25
|
+
io,
|
|
26
|
+
ops::Deref,
|
|
27
|
+
os::{fd::FromRawFd, unix::net::UnixStream},
|
|
28
|
+
str::FromStr,
|
|
29
|
+
sync::Arc,
|
|
30
|
+
time::Duration,
|
|
31
|
+
};
|
|
32
|
+
use tokio::{
|
|
33
|
+
io::AsyncReadExt,
|
|
34
|
+
net::UnixStream as TokioUnixStream,
|
|
35
|
+
sync::{mpsc::Sender, oneshot::Sender as OneshotSender, watch},
|
|
36
|
+
};
|
|
37
|
+
use tokio_util::io::ReaderStream;
|
|
38
|
+
use tracing::{info, warn};
|
|
39
|
+
|
|
40
|
+
#[magnus::wrap(class = "Itsi::HttpResponse", free_immediately, size)]
|
|
41
|
+
#[derive(Debug, Clone)]
|
|
42
|
+
pub struct ItsiHttpResponse {
|
|
43
|
+
pub inner: Arc<ResponseInner>,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
impl Deref for ItsiHttpResponse {
|
|
47
|
+
type Target = Arc<ResponseInner>;
|
|
48
|
+
|
|
49
|
+
fn deref(&self) -> &Self::Target {
|
|
50
|
+
&self.inner
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#[derive(Debug)]
|
|
55
|
+
pub struct ResponseInner {
|
|
56
|
+
pub frame_writer: RwLock<Option<Sender<Bytes>>>,
|
|
57
|
+
pub response: RwLock<Option<HttpResponse>>,
|
|
58
|
+
pub hijacked_socket: RwLock<Option<UnixStream>>,
|
|
59
|
+
pub response_sender: RwLock<Option<OneshotSender<ResponseFrame>>>,
|
|
60
|
+
pub shutdown_rx: watch::Receiver<RunningPhase>,
|
|
61
|
+
pub parts: Arc<Parts>,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[derive(Debug)]
|
|
65
|
+
pub enum ResponseFrame {
|
|
66
|
+
HttpResponse(Box<HttpResponse>),
|
|
67
|
+
HijackedResponse(ItsiHttpResponse),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
impl ItsiHttpResponse {
|
|
71
|
+
pub fn new(
|
|
72
|
+
parts: Arc<Parts>,
|
|
73
|
+
response_sender: OneshotSender<ResponseFrame>,
|
|
74
|
+
shutdown_rx: watch::Receiver<RunningPhase>,
|
|
75
|
+
) -> Self {
|
|
76
|
+
Self {
|
|
77
|
+
inner: Arc::new(ResponseInner {
|
|
78
|
+
parts,
|
|
79
|
+
shutdown_rx,
|
|
80
|
+
response_sender: RwLock::new(Some(response_sender)),
|
|
81
|
+
frame_writer: RwLock::new(None),
|
|
82
|
+
response: RwLock::new(Some(Response::new(HttpBody::empty()))),
|
|
83
|
+
hijacked_socket: RwLock::new(None),
|
|
84
|
+
}),
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async fn two_way_bridge(upgraded: Upgraded, local: TokioUnixStream) -> io::Result<()> {
|
|
89
|
+
let client_io = TokioIo::new(upgraded);
|
|
90
|
+
|
|
91
|
+
// Split each side
|
|
92
|
+
let (mut lr, mut lw) = tokio::io::split(local);
|
|
93
|
+
let (mut cr, mut cw) = tokio::io::split(client_io);
|
|
94
|
+
|
|
95
|
+
let to_ruby = tokio::spawn(async move {
|
|
96
|
+
if let Err(e) = tokio::io::copy(&mut cr, &mut lw).await {
|
|
97
|
+
eprintln!("Error copying upgraded->local: {:?}", e);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
let from_ruby = tokio::spawn(async move {
|
|
101
|
+
if let Err(e) = tokio::io::copy(&mut lr, &mut cw).await {
|
|
102
|
+
eprintln!("Error copying upgraded->local: {:?}", e);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
let _ = to_ruby.await;
|
|
107
|
+
let _ = from_ruby.await;
|
|
108
|
+
Ok(())
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async fn read_response_headers(&self, reader: &mut TokioUnixStream) -> Result<Vec<u8>> {
|
|
112
|
+
let mut buf = [0u8; 1];
|
|
113
|
+
let mut collected = Vec::new();
|
|
114
|
+
loop {
|
|
115
|
+
let n = reader.read(&mut buf).await?;
|
|
116
|
+
if n == 0 {
|
|
117
|
+
// EOF reached unexpectedly
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
collected.push(buf[0]);
|
|
121
|
+
if collected.ends_with(b"\r\n\r\n") {
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
Ok(collected)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pub async fn read_hijacked_headers(
|
|
130
|
+
&self,
|
|
131
|
+
) -> Result<(HeaderMap, StatusCode, bool, TokioUnixStream)> {
|
|
132
|
+
let hijacked_socket =
|
|
133
|
+
self.hijacked_socket
|
|
134
|
+
.write()
|
|
135
|
+
.take()
|
|
136
|
+
.ok_or(itsi_error::ItsiError::InvalidInput(
|
|
137
|
+
"Couldn't hijack stream".to_owned(),
|
|
138
|
+
))?;
|
|
139
|
+
let mut reader = TokioUnixStream::from_std(hijacked_socket).unwrap();
|
|
140
|
+
let response_headers = self.read_response_headers(&mut reader).await?;
|
|
141
|
+
let mut headers = [httparse::EMPTY_HEADER; 64];
|
|
142
|
+
let mut resp = httparse::Response::new(&mut headers);
|
|
143
|
+
resp.parse(&response_headers)?;
|
|
144
|
+
|
|
145
|
+
let status = StatusCode::from_u16(resp.code.unwrap_or(200)).unwrap_or(StatusCode::OK);
|
|
146
|
+
let mut headers = HeaderMap::new();
|
|
147
|
+
for header in resp.headers.iter() {
|
|
148
|
+
headers.insert(
|
|
149
|
+
HeaderName::from_str(header.name).unwrap(),
|
|
150
|
+
HeaderValue::from_bytes(header.value).unwrap(),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
let requires_upgrade = status == StatusCode::SWITCHING_PROTOCOLS;
|
|
154
|
+
Ok((headers, status, requires_upgrade, reader))
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
pub async fn process_hijacked_response(&self) -> Result<HttpResponse> {
|
|
158
|
+
let (headers, status, requires_upgrade, reader) = self.read_hijacked_headers().await?;
|
|
159
|
+
let mut response = if requires_upgrade {
|
|
160
|
+
let parts = self.parts.clone();
|
|
161
|
+
tokio::spawn(async move {
|
|
162
|
+
let mut req = Request::from_parts((*parts).clone(), Empty::<Bytes>::new());
|
|
163
|
+
match hyper::upgrade::on(&mut req).await {
|
|
164
|
+
Ok(upgraded) => {
|
|
165
|
+
Self::two_way_bridge(upgraded, reader)
|
|
166
|
+
.await
|
|
167
|
+
.expect("Error in creating two way bridge");
|
|
168
|
+
}
|
|
169
|
+
Err(e) => eprintln!("upgrade error: {:?}", e),
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
Response::new(HttpBody::empty())
|
|
173
|
+
} else {
|
|
174
|
+
let stream = ReaderStream::new(reader);
|
|
175
|
+
let boxed_body = if headers
|
|
176
|
+
.get(TRANSFER_ENCODING)
|
|
177
|
+
.is_some_and(|h| h == "chunked")
|
|
178
|
+
{
|
|
179
|
+
HttpBody::stream(unfold(
|
|
180
|
+
(stream, Vec::new()),
|
|
181
|
+
|(mut stream, mut buf)| async move {
|
|
182
|
+
loop {
|
|
183
|
+
if let Some(pos) = buf.iter().position(|&b| b == b'\n') {
|
|
184
|
+
let line = buf.drain(..=pos).collect::<Vec<u8>>();
|
|
185
|
+
let line = std::str::from_utf8(&line).ok()?.trim();
|
|
186
|
+
let chunk_size = usize::from_str_radix(line, 16).ok()?;
|
|
187
|
+
if chunk_size == 0 {
|
|
188
|
+
return None;
|
|
189
|
+
}
|
|
190
|
+
while buf.len() < chunk_size {
|
|
191
|
+
match stream.next().await {
|
|
192
|
+
Some(Ok(chunk)) => buf.extend_from_slice(&chunk),
|
|
193
|
+
_ => return None,
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
let data = buf.drain(..chunk_size).collect::<Vec<u8>>();
|
|
197
|
+
if buf.starts_with(b"\r\n") {
|
|
198
|
+
buf.drain(..2);
|
|
199
|
+
}
|
|
200
|
+
return Some((Ok(Bytes::from(data)), (stream, buf)));
|
|
201
|
+
}
|
|
202
|
+
match stream.next().await {
|
|
203
|
+
Some(Ok(chunk)) => buf.extend_from_slice(&chunk),
|
|
204
|
+
_ => return None,
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
))
|
|
209
|
+
} else {
|
|
210
|
+
HttpBody::stream(stream.map(|result: std::result::Result<Bytes, io::Error>| {
|
|
211
|
+
result.map_err(|e| unreachable!("unexpected io error: {:?}", e))
|
|
212
|
+
}))
|
|
213
|
+
};
|
|
214
|
+
Response::new(boxed_body)
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
*response.status_mut() = status;
|
|
218
|
+
*response.headers_mut() = headers;
|
|
219
|
+
Ok(response)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
pub fn internal_server_error(&self, message: String) {
|
|
223
|
+
error!(message);
|
|
224
|
+
self.close_write().ok();
|
|
225
|
+
if let Some(mut response) = self.response.write().take() {
|
|
226
|
+
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
|
227
|
+
if let Some(sender) = self.response_sender.write().take() {
|
|
228
|
+
sender
|
|
229
|
+
.send(ResponseFrame::HttpResponse(Box::new(response)))
|
|
230
|
+
.ok();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
pub fn service_unavailable(&self) {
|
|
236
|
+
self.close_write().ok();
|
|
237
|
+
if let Some(mut response) = self.response.write().take() {
|
|
238
|
+
*response.status_mut() = StatusCode::SERVICE_UNAVAILABLE;
|
|
239
|
+
if let Some(sender) = self.response_sender.write().take() {
|
|
240
|
+
sender
|
|
241
|
+
.send(ResponseFrame::HttpResponse(Box::new(response)))
|
|
242
|
+
.ok();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
pub fn send_frame(&self, frame: Bytes) -> MagnusResult<()> {
|
|
248
|
+
{
|
|
249
|
+
if self.frame_writer.read().is_none() && self.response.read().is_some() {
|
|
250
|
+
if let Some(mut response) = self.response.write().take() {
|
|
251
|
+
let (writer, reader) = tokio::sync::mpsc::channel::<Bytes>(256);
|
|
252
|
+
let shutdown_rx = self.shutdown_rx.clone();
|
|
253
|
+
let frame_stream = FrameStream::new(reader, shutdown_rx.clone());
|
|
254
|
+
|
|
255
|
+
let buffered =
|
|
256
|
+
BufferedStream::new(frame_stream, 32 * 1024, Duration::from_millis(10));
|
|
257
|
+
*response.body_mut() = HttpBody::stream(buffered);
|
|
258
|
+
self.frame_writer.write().replace(writer);
|
|
259
|
+
if let Some(sender) = self.response_sender.write().take() {
|
|
260
|
+
sender
|
|
261
|
+
.send(ResponseFrame::HttpResponse(Box::new(response)))
|
|
262
|
+
.ok();
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
info!("No response!");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if let Some(frame_writer) = self.frame_writer.read().as_ref() {
|
|
270
|
+
call_without_gvl(|| frame_writer.blocking_send(frame))
|
|
271
|
+
.map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)?;
|
|
272
|
+
}
|
|
273
|
+
Ok(())
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
pub fn send_and_close(&self, frame: Bytes) -> MagnusResult<()> {
|
|
277
|
+
if self.frame_writer.read().is_some() {
|
|
278
|
+
self.send_frame(frame)?;
|
|
279
|
+
self.close()?;
|
|
280
|
+
return Ok(());
|
|
281
|
+
}
|
|
282
|
+
if let Some(mut response) = self.response.write().take() {
|
|
283
|
+
if frame.is_empty() {
|
|
284
|
+
*response.body_mut() = HttpBody::empty();
|
|
285
|
+
} else {
|
|
286
|
+
*response.body_mut() = HttpBody::full(frame);
|
|
287
|
+
}
|
|
288
|
+
if let Some(sender) = self.response_sender.write().take() {
|
|
289
|
+
sender
|
|
290
|
+
.send(ResponseFrame::HttpResponse(Box::new(response)))
|
|
291
|
+
.ok();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
Ok(())
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
pub fn close_write(&self) -> MagnusResult<bool> {
|
|
299
|
+
self.frame_writer.write().take();
|
|
300
|
+
Ok(true)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
pub fn recv_frame(&self) {
|
|
304
|
+
// not implemented
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
pub fn is_closed(&self) -> bool {
|
|
308
|
+
self.response.read().is_none() && self.frame_writer.read().is_none()
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
pub fn is_hijacked(&self) -> bool {
|
|
312
|
+
self.hijacked_socket.read().is_some()
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
pub fn close(&self) -> MagnusResult<()> {
|
|
316
|
+
self.close_write()?;
|
|
317
|
+
self.close_read()?;
|
|
318
|
+
Ok(())
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
pub fn accept_str(&self) -> &str {
|
|
322
|
+
self.parts
|
|
323
|
+
.headers
|
|
324
|
+
.get(ACCEPT)
|
|
325
|
+
.and_then(|hv| hv.to_str().ok()) // handle invalid utf-8
|
|
326
|
+
.unwrap_or("application/x-www-form-urlencoded")
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
pub fn is_html(&self) -> bool {
|
|
330
|
+
self.accept_str().starts_with("text/html")
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
pub fn is_json(&self) -> bool {
|
|
334
|
+
self.accept_str().starts_with("application/json")
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
pub fn close_read(&self) -> MagnusResult<bool> {
|
|
338
|
+
Ok(true)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
pub fn reserve_headers(&self, header_count: usize) -> MagnusResult<()> {
|
|
342
|
+
if let Some(ref mut resp) = *self.response.write() {
|
|
343
|
+
resp.headers_mut().try_reserve(header_count).ok();
|
|
344
|
+
}
|
|
345
|
+
Ok(())
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
pub fn insert_header(
|
|
349
|
+
&self,
|
|
350
|
+
headers_mut: &mut HeaderMap,
|
|
351
|
+
header_name: &HeaderName,
|
|
352
|
+
value: Bytes,
|
|
353
|
+
) {
|
|
354
|
+
static MAX_SPLIT_HEADERS: usize = 100;
|
|
355
|
+
|
|
356
|
+
let mut start = 0usize;
|
|
357
|
+
let mut emitted = 0usize;
|
|
358
|
+
|
|
359
|
+
for idx in memchr_iter(b'\n', &value).chain(std::iter::once(value.len())) {
|
|
360
|
+
if idx == start {
|
|
361
|
+
start += 1;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
let mut part = value.slice(start..idx);
|
|
366
|
+
if part.ends_with(b"\r") {
|
|
367
|
+
part.truncate(part.len() - 1);
|
|
368
|
+
}
|
|
369
|
+
if let Some(&(b' ' | b'\t')) = part.first() {
|
|
370
|
+
part.advance(1);
|
|
371
|
+
}
|
|
372
|
+
if memchr(0, &part).is_some() || part.iter().any(|&b| b < 0x20) {
|
|
373
|
+
warn!("stripped control char from header {:?}", header_name);
|
|
374
|
+
start = idx + 1;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
emitted += 1;
|
|
379
|
+
if emitted > MAX_SPLIT_HEADERS {
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
let hv = unsafe { HeaderValue::from_maybe_shared_unchecked(part) };
|
|
384
|
+
headers_mut.append(header_name, hv);
|
|
385
|
+
start = idx + 1;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
pub fn add_header(&self, header_name: Bytes, value: Bytes) -> MagnusResult<()> {
|
|
390
|
+
if let Some(ref mut resp) = *self.response.write() {
|
|
391
|
+
let headers_mut = resp.headers_mut();
|
|
392
|
+
let header_name = HeaderName::from_bytes(&header_name).map_err(|e| {
|
|
393
|
+
itsi_error::ItsiError::InvalidInput(format!(
|
|
394
|
+
"Invalid header name {:?}: {:?}",
|
|
395
|
+
header_name, e
|
|
396
|
+
))
|
|
397
|
+
})?;
|
|
398
|
+
self.insert_header(headers_mut, &header_name, value);
|
|
399
|
+
}
|
|
400
|
+
Ok(())
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
pub fn add_headers(&self, headers: HashMap<Bytes, Vec<Bytes>>) -> MagnusResult<()> {
|
|
404
|
+
if let Some(ref mut resp) = *self.response.write() {
|
|
405
|
+
let headers_mut = resp.headers_mut();
|
|
406
|
+
for (name, values) in headers {
|
|
407
|
+
let header_name = HeaderName::from_bytes(&name).map_err(|e| {
|
|
408
|
+
itsi_error::ItsiError::InvalidInput(format!(
|
|
409
|
+
"Invalid header name {:?}: {:?}",
|
|
410
|
+
name, e
|
|
411
|
+
))
|
|
412
|
+
})?;
|
|
413
|
+
for value in values {
|
|
414
|
+
self.insert_header(headers_mut, &header_name, value);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
Ok(())
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
pub fn set_status(&self, status: u16) -> MagnusResult<()> {
|
|
423
|
+
if let Some(ref mut resp) = *self.response.write() {
|
|
424
|
+
*resp.status_mut() = StatusCode::from_u16(status).map_err(|e| {
|
|
425
|
+
itsi_error::ItsiError::InvalidInput(format!(
|
|
426
|
+
"Invalid status code {:?}: {:?}",
|
|
427
|
+
status, e
|
|
428
|
+
))
|
|
429
|
+
})?;
|
|
430
|
+
}
|
|
431
|
+
Ok(())
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
pub fn hijack(&self, fd: i32) -> MagnusResult<()> {
|
|
435
|
+
let stream = unsafe { UnixStream::from_raw_fd(fd) };
|
|
436
|
+
|
|
437
|
+
*self.hijacked_socket.write() = Some(stream);
|
|
438
|
+
if let Some(sender) = self.response_sender.write().take() {
|
|
439
|
+
sender
|
|
440
|
+
.send(ResponseFrame::HijackedResponse(self.clone()))
|
|
441
|
+
.ok();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
self.close()?;
|
|
445
|
+
Ok(())
|
|
446
|
+
}
|
|
447
|
+
}
|