osprey 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }