Skip to content

Commit 4dd8e59

Browse files
Support pre-created responses.
1 parent 6f63f5d commit 4dd8e59

File tree

3 files changed

+42
-55
lines changed

3 files changed

+42
-55
lines changed

ext/hyper_ruby/src/lib.rs

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -111,42 +111,7 @@ impl Server {
111111
let hyper_response = match block.call::<_, Value>([value]) {
112112
Ok(result) => {
113113
let ref_response = Obj::<Response>::try_convert(result).unwrap();
114-
115-
let mut builder = HyperResponse::builder()
116-
.status(ref_response.status);
117-
118-
// safe because the block result will only ever be called on the ruby thread
119-
let ruby = unsafe { Ruby::get_unchecked() };
120-
121-
let ruby_response_headers = ruby.get_inner(ref_response.headers);
122-
let builder_headers = builder.headers_mut().unwrap();
123-
ruby_response_headers.foreach(|key: String, value: String| {
124-
let header_name = HeaderName::try_from(key).unwrap();
125-
builder_headers.insert(header_name, value.try_into().unwrap());
126-
Ok(ForEach::Continue)
127-
}).unwrap();
128-
129-
let response_body = ruby.get_inner(ref_response.body);
130-
let response: Result<HyperResponse<Full<Bytes>>, hyper::http::Error>;
131-
132-
if response_body.len() > 0 {
133-
// safe because RString will not be cleared here before we copy the bytes into our own Vector.
134-
unsafe {
135-
// copy directly to bytes here so we don't have to worry about encoding checks
136-
let rust_body = Bytes::copy_from_slice(response_body.as_slice());
137-
response = builder.body(Full::new(rust_body));
138-
}
139-
} else {
140-
response = builder.body(Full::new(Bytes::new()));
141-
}
142-
143-
match response {
144-
Ok(response) => response,
145-
Err(e) => {
146-
println!("HTTP request build failed {:?}", e);
147-
create_error_response("Internal server error")
148-
}
149-
}
114+
ref_response.response.clone()
150115
},
151116
Err(e) => {
152117
println!("Block call failed: {:?}", e);

ext/hyper_ruby/src/response.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
1-
use magnus::{gc, DataTypeFunctions, RHash, RString, TypedData, value::Opaque};
1+
use magnus::{r_hash::ForEach, wrap, RHash, RString, Error as MagnusError};
22

3+
use hyper::{header::HeaderName, Response as HyperResponse, StatusCode};
4+
use http_body_util::Full;
5+
use bytes::Bytes;
36
// Response object returned to Ruby; holds reference to the opaque ruby types for the headers and body.
4-
#[derive(TypedData)]
5-
#[magnus(class = "HyperRuby::Response", mark)]
7+
#[wrap(class = "HyperRuby::Response")]
68
pub struct Response {
7-
pub status: u16,
8-
pub headers: Opaque<RHash>,
9-
pub body: Opaque<RString>,
10-
}
11-
12-
impl DataTypeFunctions for Response {
13-
fn mark(&self, marker: &gc::Marker) {
14-
marker.mark(self.headers);
15-
marker.mark(self.body);
16-
}
9+
pub response: HyperResponse<Full<Bytes>>
1710
}
1811

1912
impl Response {
20-
pub fn new(status: u16, headers: RHash, body: RString) -> Self {
21-
Self {
22-
status,
23-
headers: headers.into(),
24-
body: body.into(),
13+
pub fn new(status: u16, headers: RHash, body: RString) -> Result<Self, MagnusError> {
14+
let mut builder = HyperResponse::builder()
15+
.status(status);
16+
17+
let builder_headers = builder.headers_mut().unwrap();
18+
headers.foreach(|key: String, value: String| {
19+
let header_name = HeaderName::try_from(key).unwrap();
20+
builder_headers.insert(header_name, value.try_into().unwrap());
21+
Ok(ForEach::Continue)
22+
}).unwrap();
23+
24+
if body.len() > 0 {
25+
// safe because RString will not be cleared here before we copy the bytes into our own Vector.
26+
unsafe {
27+
// copy directly to bytes here so we don't have to worry about encoding checks
28+
let rust_body = Bytes::copy_from_slice(body.as_slice());
29+
match builder.body(Full::new(rust_body)) {
30+
Ok(response) => Ok(Self { response }),
31+
Err(_) => Err(MagnusError::new(magnus::exception::runtime_error(), "Failed to create response"))
32+
}
33+
}
34+
} else {
35+
match builder.body(Full::new(Bytes::new())) {
36+
Ok(response) => Ok(Self { response }),
37+
Err(_) => Err(MagnusError::new(magnus::exception::runtime_error(), "Failed to create response"))
38+
}
2539
}
2640
}
2741
}

test/test_hyper_ruby.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
require "httpx"
55

66
class TestHyperRuby < Minitest::Test
7+
8+
ACCEPT_RESPONSE = HyperRuby::Response.new(202, { 'Content-Type' => 'text/plain' }, '').freeze
9+
710
def test_that_it_has_a_version_number
811
refute_nil ::HyperRuby::VERSION
912
end
@@ -63,7 +66,7 @@ def test_unix_socket_cleans_up_socket
6366

6467
def test_blocking
6568
buffer = String.new(capacity: 1024)
66-
with_server(-> (request) { handler_to_json(request, buffer) }) do |client|
69+
with_server(-> (request) { handler_accept(request, buffer) }) do |client|
6770
gets
6871
end
6972
end
@@ -129,4 +132,9 @@ def handler_to_json(request, buffer)
129132
def handler_return_header(request, header_key)
130133
HyperRuby::Response.new(200, { 'Content-Type' => 'application/json' }, { message: request.header(header_key) }.to_json)
131134
end
135+
136+
def handler_accept(request, buffer)
137+
request.fill_body(buffer)
138+
ACCEPT_RESPONSE
139+
end
132140
end

0 commit comments

Comments
 (0)