osprey 0.1.0

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.
@@ -0,0 +1,2 @@
1
+ pub mod rack_request;
2
+ pub mod rack_response;
@@ -0,0 +1,168 @@
1
+ use crate::{RackResponse, ARRAY_PROC, ID_CALL, ID_TRANSFORM_VALUES, OSPREY};
2
+ use dashmap::DashMap;
3
+ use hyper::{
4
+ header::{HeaderName, HeaderValue},
5
+ HeaderMap,
6
+ };
7
+ use log::{debug, error};
8
+ use magnus::{
9
+ block::Proc,
10
+ r_string::FString,
11
+ value::{Opaque, OpaqueId, ReprValue},
12
+ Class, Error, RHash, Ruby, Value,
13
+ };
14
+ use std::{
15
+ cell::RefCell,
16
+ collections::HashMap,
17
+ os::{fd::IntoRawFd, unix::net::UnixStream},
18
+ sync::{Arc, LazyLock},
19
+ };
20
+ use tokio::sync::oneshot;
21
+
22
+ pub struct RackRequestInner {
23
+ pub path: String,
24
+ pub script_name: String,
25
+ pub query_string: String,
26
+ pub method: String,
27
+ pub version: String,
28
+ pub protocol: Vec<String>,
29
+ pub host: String,
30
+ pub scheme: String,
31
+ pub headers: HashMap<String, String>,
32
+ pub remote_addr: String,
33
+ pub sender: Arc<std::sync::Mutex<Option<oneshot::Sender<RackResponse>>>>,
34
+ pub port: i32,
35
+ pub stream_fd: i32,
36
+ }
37
+
38
+ #[magnus::wrap(class = "RackRequest", free_immediately, size)]
39
+ pub struct RackRequest(pub RefCell<Option<RackRequestInner>>);
40
+
41
+ unsafe impl Send for RackRequest {}
42
+
43
+ pub fn define_request_info(ruby: &magnus::Ruby) -> Result<(), Error> {
44
+ ruby.define_class("RackRequest", ruby.class_object())?;
45
+ Ok(())
46
+ }
47
+
48
+ static INTERNED_RUBY_STRINGS: LazyLock<DashMap<String, Opaque<FString>>> =
49
+ LazyLock::new(|| DashMap::new());
50
+
51
+ fn intern_ruby_str(rust_str: &str, ruby: &Ruby) -> Opaque<FString> {
52
+ if let Some(entry) = INTERNED_RUBY_STRINGS.get(rust_str) {
53
+ entry.clone()
54
+ } else {
55
+ let rstr = Opaque::from(ruby.str_new(rust_str).to_interned_str());
56
+ INTERNED_RUBY_STRINGS.insert(rust_str.to_owned(), rstr);
57
+ rstr
58
+ }
59
+ }
60
+
61
+ impl RackRequest {
62
+ pub fn process(&self, ruby: &Ruby) -> Result<(), Error> {
63
+ let req = self.0.take().unwrap();
64
+ let path_info = req.path.strip_prefix(&req.script_name).unwrap_or("");
65
+ let sender_clone = req.sender.clone();
66
+ let fd = req.stream_fd;
67
+ let hijack_proc = ruby.proc_from_fn(move |ruby, _args, _block| {
68
+ let (local_fd, remote_fd) = if fd == -1 {
69
+ let (local, remote) = UnixStream::pair()
70
+ .map_err(|_| "Failed to create ephemeral socket pair")
71
+ .unwrap();
72
+ (local.into_raw_fd() * -1, remote.into_raw_fd())
73
+ } else {
74
+ (fd * -1, fd)
75
+ };
76
+
77
+ let response_sender = sender_clone
78
+ .clone()
79
+ .lock()
80
+ .unwrap()
81
+ .take()
82
+ .expect("No response sender");
83
+ if let Err(err) = response_sender.send(RackResponse::from_parts(
84
+ local_fd as i16,
85
+ HeaderMap::new(),
86
+ ruby.qnil().as_value(),
87
+ )) {
88
+ debug!(
89
+ "(PID={}) Failed to send response: {:?}. Connection dropped.",
90
+ unsafe { libc::getpid() },
91
+ err
92
+ );
93
+ }
94
+ let io_class: magnus::RClass = magnus::eval::<magnus::RClass>("::IO").unwrap();
95
+
96
+ io_class.new_instance((remote_fd, "r+b")).unwrap()
97
+ });
98
+
99
+ let response = ruby.get_inner(&OSPREY).funcall::<OpaqueId, (
100
+ Vec<&str>,
101
+ Opaque<FString>,
102
+ Opaque<FString>,
103
+ Opaque<FString>,
104
+ Opaque<FString>,
105
+ Opaque<FString>,
106
+ Vec<String>,
107
+ i32,
108
+ i32,
109
+ HashMap<String, String>,
110
+ Proc,
111
+ ), (i16, RHash, Value)>(
112
+ *ID_CALL,
113
+ (
114
+ vec![&req.query_string, &req.remote_addr, &path_info],
115
+ intern_ruby_str(&req.script_name, ruby),
116
+ intern_ruby_str(&req.method, ruby),
117
+ intern_ruby_str(&req.host, ruby),
118
+ intern_ruby_str(&req.scheme, ruby),
119
+ intern_ruby_str(&req.version, ruby),
120
+ req.protocol,
121
+ req.port,
122
+ req.stream_fd,
123
+ req.headers,
124
+ hijack_proc,
125
+ ),
126
+ );
127
+
128
+ if let Some(response_sender) = req.sender.lock().unwrap().take() {
129
+ let (status, headers, body): (i16, HeaderMap, Value) = match response {
130
+ Ok(res) => {
131
+ let (status, headers, body) = res;
132
+ let header_iter = headers
133
+ .funcall_with_block::<OpaqueId, (), HashMap<String, Vec<String>>>(
134
+ *ID_TRANSFORM_VALUES,
135
+ (),
136
+ ruby.get_inner(&ARRAY_PROC),
137
+ )
138
+ .unwrap()
139
+ .into_iter()
140
+ .flat_map(|(key, values)| {
141
+ values.into_iter().map(move |value| {
142
+ (
143
+ HeaderName::from_bytes(key.as_bytes()).unwrap(),
144
+ HeaderValue::from_bytes(value.as_bytes()).unwrap(),
145
+ )
146
+ })
147
+ });
148
+
149
+ (status, HeaderMap::from_iter(header_iter), body)
150
+ }
151
+ Err(err) => {
152
+ error!("Error in Ruby response: {:?}", err);
153
+ (500, HeaderMap::new(), ruby.qnil().as_value())
154
+ }
155
+ };
156
+
157
+ if let Err(err) = response_sender.send(RackResponse::from_parts(status, headers, body))
158
+ {
159
+ debug!(
160
+ "(PID={}) Failed to send response: {:?}. Connection dropped.",
161
+ unsafe { libc::getpid() },
162
+ err
163
+ );
164
+ }
165
+ }
166
+ Ok(())
167
+ }
168
+ }
@@ -0,0 +1,152 @@
1
+ use bytes::{Bytes, BytesMut};
2
+ use http_body_util::{combinators::BoxBody, Full};
3
+ use hyper::{HeaderMap, Response, StatusCode};
4
+ use magnus::{
5
+ function, method, try_convert,
6
+ value::{LazyId, OpaqueId, ReprValue},
7
+ Error, IntoValue, Module, Object, Value,
8
+ };
9
+ use std::{
10
+ convert::Infallible,
11
+ sync::{Arc, Mutex},
12
+ };
13
+
14
+ #[derive(Debug, Clone)]
15
+ pub struct RackResponse {
16
+ pub status: i16,
17
+ pub headers: HeaderMap,
18
+ pub body: Bytes,
19
+ }
20
+
21
+ static ID_EACH: LazyId = LazyId::new("each");
22
+ static ID_CALL: LazyId = LazyId::new("call");
23
+ static ID_CLOSE: LazyId = LazyId::new("close");
24
+ static ID_TO_ENUM: LazyId = LazyId::new("to_enum");
25
+ static ID_INJECT: LazyId = LazyId::new("inject");
26
+ static ID_TO_ARY: LazyId = LazyId::new("to_ary");
27
+ static ID_CONCAT: LazyId = LazyId::new("<<");
28
+
29
+ impl RackResponse {
30
+ pub fn from_parts(status: i16, headers: HeaderMap, body: Value) -> Self {
31
+ let body = unsafe { RackResponse::gather_body(&body).unwrap_or_default() };
32
+ Self {
33
+ status,
34
+ headers,
35
+ body,
36
+ }
37
+ }
38
+
39
+ pub fn into_hyper_response(self) -> Response<BoxBody<Bytes, Infallible>> {
40
+ let mut response = Response::new(BoxBody::new(Full::new(self.body)));
41
+ *response.status_mut() = StatusCode::from_u16(self.status as u16).unwrap();
42
+ *response.headers_mut() = self.headers;
43
+ response
44
+ }
45
+
46
+ unsafe fn gather_body(body_value: &Value) -> Result<Bytes, Error> {
47
+ if body_value.respond_to(*ID_EACH, false)? {
48
+ let bytes: Bytes = body_value
49
+ .funcall::<OpaqueId, (OpaqueId,), Value>(*ID_TO_ENUM, (*ID_EACH,))
50
+ .unwrap()
51
+ .funcall::<OpaqueId, (OpaqueId,), Value>(*ID_INJECT, (*ID_CONCAT,))
52
+ .unwrap()
53
+ .to_r_string()
54
+ .unwrap()
55
+ .to_bytes();
56
+
57
+ body_value.check_funcall::<OpaqueId, (), Value>(*ID_CLOSE, ());
58
+ return Ok(bytes);
59
+ }
60
+
61
+ // 2) If body responds to `to_ary`, it can be treated like an array of strings
62
+ if let Some(Ok(array)) = body_value.check_funcall::<_, (), Vec<Bytes>>(*ID_TO_ARY, ()) {
63
+ body_value.check_funcall::<OpaqueId, (), Value>(*ID_CLOSE, ());
64
+ return Ok(array.into_iter().flatten().collect());
65
+ }
66
+
67
+ // 3) If body responds to `call`, treat it like a Streaming Body
68
+ if body_value.respond_to(*ID_CALL, false)? {
69
+ let collector = make_collector()?;
70
+ let _: Option<Value> = body_value.funcall(*ID_CALL, (collector,))?;
71
+ let data = get_collector_bytes(&collector)?;
72
+ body_value.check_funcall::<OpaqueId, (), Value>(*ID_CLOSE, ());
73
+ return Ok(data);
74
+ }
75
+
76
+ // 4) If none of the above apply, stringify the body and return it
77
+ let bytes = body_value.to_r_string().unwrap().to_bytes();
78
+ body_value.check_funcall::<OpaqueId, (), Value>(*ID_CLOSE, ());
79
+ Ok(bytes)
80
+ }
81
+ }
82
+
83
+ #[magnus::wrap(class = "BodyCollector")]
84
+ struct BodyCollector {
85
+ buffer: Arc<Mutex<BytesMut>>,
86
+ }
87
+
88
+ impl BodyCollector {
89
+ fn new() -> Self {
90
+ BodyCollector {
91
+ buffer: Arc::new(Mutex::new(BytesMut::new())),
92
+ }
93
+ }
94
+
95
+ // e.g. def write(str)
96
+ fn write(&self, s: Bytes) -> Result<(), Error> {
97
+ self.buffer
98
+ .lock()
99
+ .expect("Unlock body collector buffer")
100
+ .extend(&s);
101
+ Ok(())
102
+ }
103
+
104
+ // def <<(str)
105
+ fn append(&self, s: Bytes) -> Result<Self, Error> {
106
+ self.buffer
107
+ .lock()
108
+ .expect("Unlock body collector buffer")
109
+ .extend(&s);
110
+ Ok(Self {
111
+ buffer: Arc::clone(&self.buffer),
112
+ })
113
+ }
114
+
115
+ fn flush(&self) -> Result<(), Error> {
116
+ Ok(())
117
+ }
118
+
119
+ fn close(&self) -> Result<(), Error> {
120
+ Ok(())
121
+ }
122
+
123
+ fn get_buffer(&self) -> Bytes {
124
+ self.buffer
125
+ .lock()
126
+ .expect("Unlock body collector buffer")
127
+ .clone()
128
+ .freeze()
129
+ }
130
+ }
131
+
132
+ // Then to create a Ruby object of this class:
133
+ fn make_collector() -> Result<Value, Error> {
134
+ let collector = BodyCollector::new();
135
+ Ok(collector.into_value())
136
+ }
137
+
138
+ fn get_collector_bytes(collector_val: &Value) -> Result<Bytes, Error> {
139
+ let collector: &BodyCollector = try_convert::TryConvert::try_convert(*collector_val)?;
140
+ Ok(collector.get_buffer())
141
+ }
142
+
143
+ pub fn define_collector(ruby: &magnus::Ruby) -> Result<(), Error> {
144
+ let class = ruby.define_class("BodyCollector", ruby.class_object())?;
145
+ class.define_singleton_method("new", function!(BodyCollector::new, 0))?;
146
+ class.define_method("write", method!(BodyCollector::write, 1))?;
147
+ class.define_method("append", method!(BodyCollector::append, 1))?;
148
+ class.define_method("flush", method!(BodyCollector::flush, 0))?;
149
+ class.define_method("close", method!(BodyCollector::close, 0))?;
150
+
151
+ Ok(())
152
+ }