11use magnus:: block:: block_proc;
22use magnus:: r_hash:: ForEach ;
33use magnus:: typed_data:: Obj ;
4- use magnus:: value:: Opaque ;
4+ use magnus:: value:: { qnil , Opaque } ;
55use magnus:: { function, gc, method, prelude:: * , DataTypeFunctions , Error as MagnusError , RString , Ruby , TypedData , Value } ;
66use bytes:: Bytes ;
77
@@ -35,15 +35,56 @@ impl ServerConfig {
3535 }
3636}
3737
38+ struct RequestWithCompletion {
39+ request : Request ,
40+ // sent a response back on this thread
41+ response_tx : oneshot:: Sender < WarpResponse < Bytes > > ,
42+ }
43+
3844// Request type that will be sent to worker threads
3945#[ derive( Debug ) ]
40- struct WorkRequest {
46+ #[ magnus:: wrap( class = "HyperRuby::Request" ) ]
47+ struct Request {
4148 method : warp:: http:: Method ,
4249 path : String ,
4350 headers : warp:: http:: HeaderMap ,
4451 body : Bytes ,
45- // sent a response back on this thread
46- response_tx : oneshot:: Sender < WarpResponse < Vec < u8 > > > ,
52+ }
53+
54+ impl Request {
55+ pub fn method ( & self ) -> String {
56+ self . method . to_string ( )
57+ }
58+
59+ pub fn path ( & self ) -> RString {
60+ RString :: new ( & self . path )
61+ }
62+
63+ pub fn header ( & self , key : String ) -> Value {
64+ match self . headers . get ( key) {
65+ Some ( value) => match value. to_str ( ) {
66+ Ok ( value) => RString :: new ( value) . as_value ( ) ,
67+ Err ( _) => qnil ( ) . as_value ( ) ,
68+ } ,
69+ None => qnil ( ) . as_value ( ) ,
70+ }
71+ }
72+
73+ pub fn body_size ( & self ) -> usize {
74+ self . body . len ( )
75+ }
76+
77+ pub fn body ( & self ) -> Value {
78+ if self . body . is_empty ( ) {
79+ return qnil ( ) . as_value ( ) ;
80+ }
81+
82+ let result = RString :: buf_new ( self . body_size ( ) ) ;
83+
84+ // cat to the end of the string directly from the byte buffer
85+ result. cat ( self . body . as_ref ( ) ) ;
86+ result. as_value ( )
87+ }
4788}
4889
4990
@@ -76,8 +117,8 @@ impl Response {
76117struct Server {
77118 server_handle : Arc < Mutex < Option < JoinHandle < ( ) > > > > ,
78119 config : RefCell < ServerConfig > ,
79- work_rx : RefCell < Option < crossbeam_channel:: Receiver < WorkRequest > > > ,
80- work_tx : RefCell < Option < Arc < crossbeam_channel:: Sender < WorkRequest > > > > ,
120+ work_rx : RefCell < Option < crossbeam_channel:: Receiver < RequestWithCompletion > > > ,
121+ work_tx : RefCell < Option < Arc < crossbeam_channel:: Sender < RequestWithCompletion > > > > ,
81122}
82123
83124
@@ -134,39 +175,20 @@ impl Server {
134175
135176 match work_request {
136177 Ok ( work_request) => {
137-
138- // Create Ruby hash with request data
139- let req_hash = magnus:: RHash :: new ( ) ;
140- req_hash. aset ( magnus:: Symbol :: new ( "method" ) , work_request. method . to_string ( ) ) ?;
141- req_hash. aset ( magnus:: Symbol :: new ( "path" ) , work_request. path ) ?;
142-
143- // Convert headers to Ruby hash
144- let headers_hash = magnus:: RHash :: new ( ) ;
145- for ( key, value) in work_request. headers . iter ( ) {
146- if let Ok ( value_str) = value. to_str ( ) {
147- headers_hash. aset ( key. as_str ( ) , value_str) ?;
148- }
149- }
150- req_hash. aset ( magnus:: Symbol :: new ( "headers" ) , headers_hash) ?;
151-
152- // Convert body to Ruby string
153- req_hash. aset ( magnus:: Symbol :: new ( "body" ) , magnus:: RString :: from_slice ( & work_request. body [ ..] ) ) ?;
154-
155178 // Call the Ruby block and handle the response
156- let warp_response = match block. call :: < _ , Value > ( [ req_hash ] ) {
179+ let warp_response = match block. call :: < _ , Value > ( ( work_request . request , ) ) {
157180 Ok ( result) => {
158181 let ref_response = Obj :: < Response > :: try_convert ( result) . unwrap ( ) ;
159182
160- let mut response: WarpResponse < Vec < u8 > > ;
183+ let mut response: WarpResponse < Bytes > ;
161184 let ruby = Ruby :: get ( ) . unwrap ( ) ; // errors on non-Ruby thread
162185 let response_body = ruby. get_inner ( ref_response. body ) ;
163186 let ruby_response_headers = ruby. get_inner ( ref_response. headers ) ;
164187
165188 // safe because RString will not be cleared here before we copy the bytes into our own Vector.
166189 unsafe {
167190 // copy directly to bytes here so we don't have to worry about encoding checks
168- let rust_body = Vec :: from ( response_body. as_slice ( ) ) ;
169-
191+ let rust_body = Bytes :: copy_from_slice ( response_body. as_slice ( ) ) ;
170192 response = WarpResponse :: new ( rust_body) ;
171193 }
172194
@@ -287,8 +309,8 @@ impl Server {
287309
288310
289311// Helper function to create error responses
290- fn create_error_response ( error_message : & str ) -> WarpResponse < Vec < u8 > > {
291- let mut response = WarpResponse :: new ( format ! ( r#"{{"error": "{}"}}"# , error_message) . into_bytes ( ) ) ;
312+ fn create_error_response ( error_message : & str ) -> WarpResponse < Bytes > {
313+ let mut response = WarpResponse :: new ( Bytes :: from ( format ! ( r#"{{"error": "{}"}}"# , error_message) ) ) ;
292314 * response. status_mut ( ) = warp:: http:: StatusCode :: INTERNAL_SERVER_ERROR ;
293315 response. headers_mut ( ) . insert (
294316 warp:: http:: header:: CONTENT_TYPE ,
@@ -302,19 +324,23 @@ async fn handle_request(
302324 path : warp:: path:: FullPath ,
303325 headers : warp:: http:: HeaderMap ,
304326 body : Bytes ,
305- work_tx : Arc < crossbeam_channel:: Sender < WorkRequest > > ,
306- ) -> Result < WarpResponse < Vec < u8 > > , warp:: Rejection > {
327+ work_tx : Arc < crossbeam_channel:: Sender < RequestWithCompletion > > ,
328+ ) -> Result < WarpResponse < Bytes > , warp:: Rejection > {
307329 let ( response_tx, response_rx) = oneshot:: channel ( ) ;
308330
309- let work_request = WorkRequest {
331+ let request = Request {
310332 method,
311333 path : path. as_str ( ) . to_string ( ) ,
312334 headers,
313- body,
335+ body
336+ } ;
337+
338+ let with_completion = RequestWithCompletion {
339+ request,
314340 response_tx,
315341 } ;
316342
317- if let Err ( _) = work_tx. send ( work_request ) {
343+ if let Err ( _) = work_tx. send ( with_completion ) {
318344 return Err ( warp:: reject:: reject ( ) ) ;
319345 }
320346
@@ -338,5 +364,12 @@ fn init(ruby: &Ruby) -> Result<(), MagnusError> {
338364 let response_class = module. define_class ( "Response" , ruby. class_object ( ) ) ?;
339365 response_class. define_singleton_method ( "new" , function ! ( Response :: new, 3 ) ) ?;
340366
367+ let request_class = module. define_class ( "Request" , ruby. class_object ( ) ) ?;
368+ request_class. define_method ( "http_method" , method ! ( Request :: method, 0 ) ) ?;
369+ request_class. define_method ( "path" , method ! ( Request :: path, 0 ) ) ?;
370+ request_class. define_method ( "header" , method ! ( Request :: header, 1 ) ) ?;
371+ request_class. define_method ( "body" , method ! ( Request :: body, 0 ) ) ?;
372+ request_class. define_method ( "body_size" , method ! ( Request :: body_size, 0 ) ) ?;
373+
341374 Ok ( ( ) )
342375}
0 commit comments