spikard 0.7.4 → 0.8.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.
- checksums.yaml +4 -4
- data/ext/spikard_rb/Cargo.lock +583 -201
- data/ext/spikard_rb/Cargo.toml +1 -1
- data/lib/spikard/grpc.rb +182 -0
- data/lib/spikard/version.rb +1 -1
- data/lib/spikard.rb +1 -1
- data/vendor/crates/spikard-bindings-shared/Cargo.toml +2 -1
- data/vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs +197 -0
- data/vendor/crates/spikard-bindings-shared/src/lib.rs +2 -0
- data/vendor/crates/spikard-core/Cargo.toml +1 -1
- data/vendor/crates/spikard-http/Cargo.toml +5 -1
- data/vendor/crates/spikard-http/src/grpc/handler.rs +260 -0
- data/vendor/crates/spikard-http/src/grpc/mod.rs +342 -0
- data/vendor/crates/spikard-http/src/grpc/service.rs +392 -0
- data/vendor/crates/spikard-http/src/grpc/streaming.rs +237 -0
- data/vendor/crates/spikard-http/src/lib.rs +14 -0
- data/vendor/crates/spikard-http/src/server/grpc_routing.rs +288 -0
- data/vendor/crates/spikard-http/src/server/mod.rs +1 -0
- data/vendor/crates/spikard-http/tests/common/grpc_helpers.rs +1023 -0
- data/vendor/crates/spikard-http/tests/common/mod.rs +8 -0
- data/vendor/crates/spikard-http/tests/grpc_error_handling_test.rs +653 -0
- data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +332 -0
- data/vendor/crates/spikard-http/tests/grpc_metadata_test.rs +518 -0
- data/vendor/crates/spikard-http/tests/grpc_server_integration.rs +476 -0
- data/vendor/crates/spikard-rb/Cargo.toml +2 -1
- data/vendor/crates/spikard-rb/src/config/server_config.rs +1 -0
- data/vendor/crates/spikard-rb/src/grpc/handler.rs +352 -0
- data/vendor/crates/spikard-rb/src/grpc/mod.rs +9 -0
- data/vendor/crates/spikard-rb/src/lib.rs +4 -0
- data/vendor/crates/spikard-rb-macros/Cargo.toml +1 -1
- metadata +15 -1
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
//! Ruby gRPC handler implementation using Magnus FFI
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides a bridge between Ruby code implementing gRPC handlers
|
|
4
|
+
//! and Spikard's Rust-based gRPC runtime. It handles serialization/deserialization
|
|
5
|
+
//! of protobuf messages as binary strings.
|
|
6
|
+
|
|
7
|
+
use bytes::Bytes;
|
|
8
|
+
use magnus::prelude::*;
|
|
9
|
+
use magnus::value::{InnerValue, Opaque};
|
|
10
|
+
use magnus::{Error, RHash, RString, Ruby, Symbol, TryConvert, Value, gc::Marker};
|
|
11
|
+
use spikard_bindings_shared::grpc_metadata::{extract_metadata_to_hashmap, hashmap_to_metadata};
|
|
12
|
+
use spikard_http::grpc::{GrpcHandler, GrpcHandlerResult, GrpcRequestData, GrpcResponseData};
|
|
13
|
+
use std::cell::RefCell;
|
|
14
|
+
use std::collections::HashMap;
|
|
15
|
+
use std::future::Future;
|
|
16
|
+
use std::panic::AssertUnwindSafe;
|
|
17
|
+
use std::pin::Pin;
|
|
18
|
+
use std::sync::Arc;
|
|
19
|
+
|
|
20
|
+
use crate::gvl::with_gvl;
|
|
21
|
+
|
|
22
|
+
/// Ruby-facing gRPC request object
|
|
23
|
+
///
|
|
24
|
+
/// This struct is exposed to Ruby code and contains the parsed components
|
|
25
|
+
/// of a gRPC request. The payload is provided as a binary string that Ruby
|
|
26
|
+
/// code can deserialize using the google-protobuf gem.
|
|
27
|
+
#[derive(Debug, Clone)]
|
|
28
|
+
#[magnus::wrap(class = "Spikard::Grpc::Request", free_immediately)]
|
|
29
|
+
pub struct RubyGrpcRequest {
|
|
30
|
+
service_name: String,
|
|
31
|
+
method_name: String,
|
|
32
|
+
payload: Vec<u8>,
|
|
33
|
+
metadata: HashMap<String, String>,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
impl RubyGrpcRequest {
|
|
37
|
+
/// Create a new RubyGrpcRequest from GrpcRequestData
|
|
38
|
+
fn from_grpc_request(request: GrpcRequestData) -> Self {
|
|
39
|
+
let metadata = extract_metadata_to_hashmap(&request.metadata, true);
|
|
40
|
+
Self {
|
|
41
|
+
service_name: request.service_name,
|
|
42
|
+
method_name: request.method_name,
|
|
43
|
+
payload: request.payload.to_vec(),
|
|
44
|
+
metadata,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Get the service name
|
|
49
|
+
fn service_name(&self) -> &str {
|
|
50
|
+
&self.service_name
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Get the method name
|
|
54
|
+
fn method_name(&self) -> &str {
|
|
55
|
+
&self.method_name
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Get the payload as a binary string
|
|
59
|
+
fn payload(ruby: &Ruby, rb_self: &Self) -> Value {
|
|
60
|
+
ruby.str_from_slice(&rb_self.payload).as_value()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Get metadata as a Ruby hash
|
|
64
|
+
fn metadata(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
|
|
65
|
+
let hash = ruby.hash_new();
|
|
66
|
+
for (key, value) in &rb_self.metadata {
|
|
67
|
+
hash.aset(ruby.str_new(key), ruby.str_new(value))?;
|
|
68
|
+
}
|
|
69
|
+
Ok(hash.as_value())
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Ruby-facing gRPC response object
|
|
74
|
+
///
|
|
75
|
+
/// Ruby code creates instances of this class to return gRPC responses.
|
|
76
|
+
/// The payload should be a binary string containing the serialized protobuf message.
|
|
77
|
+
#[derive(Debug, Clone, Default)]
|
|
78
|
+
#[magnus::wrap(class = "Spikard::Grpc::Response", free_immediately)]
|
|
79
|
+
pub struct RubyGrpcResponse {
|
|
80
|
+
payload: RefCell<Vec<u8>>,
|
|
81
|
+
metadata: RefCell<HashMap<String, String>>,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
impl RubyGrpcResponse {
|
|
85
|
+
/// Initialize the response with a payload (called by Ruby's new)
|
|
86
|
+
fn initialize(&self, args: &[Value]) -> Result<(), Error> {
|
|
87
|
+
// Handle both positional and keyword arguments
|
|
88
|
+
let payload_value = if args.is_empty() {
|
|
89
|
+
return Err(Error::new(magnus::exception::arg_error(), "missing keyword: payload"));
|
|
90
|
+
} else if args.len() == 1 {
|
|
91
|
+
// Check if it's a hash (keyword args) or a string (positional arg)
|
|
92
|
+
if let Ok(hash) = RHash::try_convert(args[0]) {
|
|
93
|
+
// Keyword arguments: { payload: "data" }
|
|
94
|
+
hash.get(Symbol::new("payload"))
|
|
95
|
+
.ok_or_else(|| Error::new(magnus::exception::arg_error(), "missing keyword: payload"))?
|
|
96
|
+
} else {
|
|
97
|
+
// Positional argument: "data"
|
|
98
|
+
args[0]
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
return Err(Error::new(magnus::exception::arg_error(), "wrong number of arguments"));
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
let payload_str = RString::try_convert(payload_value).map_err(|_| {
|
|
105
|
+
Error::new(magnus::exception::arg_error(), "payload must be a String (binary)")
|
|
106
|
+
})?;
|
|
107
|
+
|
|
108
|
+
let payload_bytes = unsafe { payload_str.as_slice() }.to_vec();
|
|
109
|
+
|
|
110
|
+
*self.payload.borrow_mut() = payload_bytes;
|
|
111
|
+
*self.metadata.borrow_mut() = HashMap::new();
|
|
112
|
+
Ok(())
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// Set metadata on the response
|
|
116
|
+
fn set_metadata(&self, metadata: Value) -> Result<(), Error> {
|
|
117
|
+
if metadata.is_nil() {
|
|
118
|
+
return Ok(());
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let hash = RHash::try_convert(metadata)?;
|
|
122
|
+
let metadata_map = hash.to_hash_map::<String, String>()?;
|
|
123
|
+
*self.metadata.borrow_mut() = metadata_map;
|
|
124
|
+
Ok(())
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// Get the payload
|
|
128
|
+
fn payload(ruby: &Ruby, rb_self: &Self) -> Value {
|
|
129
|
+
ruby.str_from_slice(&rb_self.payload.borrow()).as_value()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Get metadata as a Ruby hash
|
|
133
|
+
fn get_metadata(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
|
|
134
|
+
let hash = ruby.hash_new();
|
|
135
|
+
for (key, value) in rb_self.metadata.borrow().iter() {
|
|
136
|
+
hash.aset(ruby.str_new(key), ruby.str_new(value))?;
|
|
137
|
+
}
|
|
138
|
+
Ok(hash.as_value())
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// Convert to GrpcResponseData
|
|
142
|
+
fn into_grpc_response(self) -> Result<GrpcResponseData, String> {
|
|
143
|
+
let metadata = hashmap_to_metadata(&self.metadata.borrow())?;
|
|
144
|
+
|
|
145
|
+
Ok(GrpcResponseData {
|
|
146
|
+
payload: Bytes::from(self.payload.borrow().clone()),
|
|
147
|
+
metadata,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// Ruby gRPC handler wrapper
|
|
153
|
+
///
|
|
154
|
+
/// Wraps a Ruby handler object and implements the GrpcHandler trait,
|
|
155
|
+
/// allowing Ruby code to handle gRPC requests.
|
|
156
|
+
#[derive(Clone)]
|
|
157
|
+
pub struct RubyGrpcHandler {
|
|
158
|
+
inner: Arc<RubyGrpcHandlerInner>,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
struct RubyGrpcHandlerInner {
|
|
162
|
+
handler: Opaque<Value>,
|
|
163
|
+
service_name: String,
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
impl RubyGrpcHandler {
|
|
167
|
+
/// Create a new RubyGrpcHandler
|
|
168
|
+
///
|
|
169
|
+
/// # Arguments
|
|
170
|
+
///
|
|
171
|
+
/// * `handler` - A Ruby object that responds to `handle_request(request)`
|
|
172
|
+
/// * `service_name` - The fully qualified service name (e.g., "mypackage.MyService")
|
|
173
|
+
#[allow(dead_code)]
|
|
174
|
+
pub fn new(handler: Value, service_name: String) -> Self {
|
|
175
|
+
Self {
|
|
176
|
+
inner: Arc::new(RubyGrpcHandlerInner {
|
|
177
|
+
handler: Opaque::from(handler),
|
|
178
|
+
service_name,
|
|
179
|
+
}),
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// Required by Ruby GC; invoked through the magnus mark hook.
|
|
184
|
+
#[allow(dead_code)]
|
|
185
|
+
pub fn mark(&self, marker: &Marker) {
|
|
186
|
+
if let Ok(ruby) = Ruby::get() {
|
|
187
|
+
let handler_val = self.inner.handler.get_inner_with(&ruby);
|
|
188
|
+
marker.mark(handler_val);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Handle a gRPC request by calling into Ruby
|
|
193
|
+
fn handle_request(&self, request: GrpcRequestData) -> GrpcHandlerResult {
|
|
194
|
+
with_gvl(|| {
|
|
195
|
+
let result = std::panic::catch_unwind(AssertUnwindSafe(|| self.handle_request_inner(request)));
|
|
196
|
+
match result {
|
|
197
|
+
Ok(res) => res,
|
|
198
|
+
Err(_) => Err(tonic::Status::internal(
|
|
199
|
+
"Unexpected panic while executing Ruby gRPC handler",
|
|
200
|
+
)),
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fn handle_request_inner(&self, request: GrpcRequestData) -> GrpcHandlerResult {
|
|
206
|
+
let ruby = Ruby::get().map_err(|_| {
|
|
207
|
+
tonic::Status::internal("Ruby VM unavailable while invoking gRPC handler")
|
|
208
|
+
})?;
|
|
209
|
+
|
|
210
|
+
// Convert request to Ruby object
|
|
211
|
+
let ruby_request = RubyGrpcRequest::from_grpc_request(request);
|
|
212
|
+
let request_value = ruby
|
|
213
|
+
.obj_wrap(ruby_request)
|
|
214
|
+
.as_value();
|
|
215
|
+
|
|
216
|
+
// Call Ruby handler
|
|
217
|
+
let handler_value = self.inner.handler.get_inner_with(&ruby);
|
|
218
|
+
let response_value = handler_value
|
|
219
|
+
.funcall::<_, _, Value>("handle_request", (request_value,))
|
|
220
|
+
.map_err(|err| {
|
|
221
|
+
tonic::Status::internal(format!("Ruby gRPC handler failed: {}", err))
|
|
222
|
+
})?;
|
|
223
|
+
|
|
224
|
+
// Convert Ruby response to GrpcResponseData
|
|
225
|
+
let ruby_response = <&RubyGrpcResponse>::try_convert(response_value)
|
|
226
|
+
.map_err(|err| {
|
|
227
|
+
tonic::Status::internal(format!(
|
|
228
|
+
"Handler must return Spikard::Grpc::Response, got error: {}",
|
|
229
|
+
err
|
|
230
|
+
))
|
|
231
|
+
})?;
|
|
232
|
+
|
|
233
|
+
ruby_response
|
|
234
|
+
.clone()
|
|
235
|
+
.into_grpc_response()
|
|
236
|
+
.map_err(|err| tonic::Status::internal(format!("Failed to build gRPC response: {}", err)))
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
impl GrpcHandler for RubyGrpcHandler {
|
|
241
|
+
fn call(&self, request: GrpcRequestData) -> Pin<Box<dyn Future<Output = GrpcHandlerResult> + Send>> {
|
|
242
|
+
let handler = self.clone();
|
|
243
|
+
Box::pin(async move { handler.handle_request(request) })
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
fn service_name(&self) -> &'static str {
|
|
247
|
+
// We need to return a 'static str, but we have a String.
|
|
248
|
+
// For now, we'll leak the string to get a 'static reference.
|
|
249
|
+
// This is acceptable because service names are registered once at startup.
|
|
250
|
+
Box::leak(self.inner.service_name.clone().into_boxed_str())
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/// Initialize the gRPC module in Ruby
|
|
255
|
+
pub fn init(ruby: &Ruby, spikard_module: &magnus::RModule) -> Result<(), Error> {
|
|
256
|
+
let grpc_module = spikard_module.define_module("Grpc")?;
|
|
257
|
+
|
|
258
|
+
// Define Spikard::Grpc::Request class
|
|
259
|
+
let request_class = grpc_module.define_class("Request", ruby.class_object())?;
|
|
260
|
+
request_class.define_method("service_name", magnus::method!(RubyGrpcRequest::service_name, 0))?;
|
|
261
|
+
request_class.define_method("method_name", magnus::method!(RubyGrpcRequest::method_name, 0))?;
|
|
262
|
+
request_class.define_method("payload", magnus::method!(RubyGrpcRequest::payload, 0))?;
|
|
263
|
+
request_class.define_method("metadata", magnus::method!(RubyGrpcRequest::metadata, 0))?;
|
|
264
|
+
|
|
265
|
+
// Define Spikard::Grpc::Response class
|
|
266
|
+
let response_class = grpc_module.define_class("Response", ruby.class_object())?;
|
|
267
|
+
response_class.define_alloc_func::<RubyGrpcResponse>();
|
|
268
|
+
response_class.define_method("initialize", magnus::method!(RubyGrpcResponse::initialize, -1))?;
|
|
269
|
+
response_class.define_method("metadata=", magnus::method!(RubyGrpcResponse::set_metadata, 1))?;
|
|
270
|
+
response_class.define_method("metadata", magnus::method!(RubyGrpcResponse::get_metadata, 0))?;
|
|
271
|
+
response_class.define_method("payload", magnus::method!(RubyGrpcResponse::payload, 0))?;
|
|
272
|
+
|
|
273
|
+
Ok(())
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#[cfg(test)]
|
|
277
|
+
mod tests {
|
|
278
|
+
use super::*;
|
|
279
|
+
use bytes::Bytes;
|
|
280
|
+
use tonic::metadata::MetadataMap;
|
|
281
|
+
|
|
282
|
+
#[test]
|
|
283
|
+
fn test_ruby_grpc_request_creation() {
|
|
284
|
+
let request = GrpcRequestData {
|
|
285
|
+
service_name: "test.TestService".to_string(),
|
|
286
|
+
method_name: "TestMethod".to_string(),
|
|
287
|
+
payload: Bytes::from("test payload"),
|
|
288
|
+
metadata: MetadataMap::new(),
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
let ruby_request = RubyGrpcRequest::from_grpc_request(request);
|
|
292
|
+
assert_eq!(ruby_request.service_name, "test.TestService");
|
|
293
|
+
assert_eq!(ruby_request.method_name, "TestMethod");
|
|
294
|
+
assert_eq!(ruby_request.payload, b"test payload");
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
#[test]
|
|
298
|
+
fn test_metadata_extraction() {
|
|
299
|
+
use spikard_bindings_shared::grpc_metadata::extract_metadata_to_hashmap;
|
|
300
|
+
|
|
301
|
+
let mut metadata = MetadataMap::new();
|
|
302
|
+
metadata.insert("content-type", "application/grpc".parse().unwrap());
|
|
303
|
+
metadata.insert("authorization", "Bearer token123".parse().unwrap());
|
|
304
|
+
|
|
305
|
+
let extracted = extract_metadata_to_hashmap(&metadata, false);
|
|
306
|
+
assert_eq!(extracted.get("content-type").unwrap(), "application/grpc");
|
|
307
|
+
assert_eq!(extracted.get("authorization").unwrap(), "Bearer token123");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
#[test]
|
|
311
|
+
fn test_grpc_response_conversion() {
|
|
312
|
+
let response = RubyGrpcResponse {
|
|
313
|
+
payload: RefCell::new(b"test response".to_vec()),
|
|
314
|
+
metadata: RefCell::new(HashMap::new()),
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
let grpc_response = response.into_grpc_response();
|
|
318
|
+
assert!(grpc_response.is_ok());
|
|
319
|
+
let grpc_response = grpc_response.unwrap();
|
|
320
|
+
assert_eq!(grpc_response.payload, Bytes::from("test response"));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
#[test]
|
|
324
|
+
fn test_grpc_response_with_metadata() {
|
|
325
|
+
let mut metadata = HashMap::new();
|
|
326
|
+
metadata.insert("x-custom-header".to_string(), "custom-value".to_string());
|
|
327
|
+
|
|
328
|
+
let response = RubyGrpcResponse {
|
|
329
|
+
payload: RefCell::new(b"test".to_vec()),
|
|
330
|
+
metadata: RefCell::new(metadata),
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
let grpc_response = response.into_grpc_response();
|
|
334
|
+
assert!(grpc_response.is_ok());
|
|
335
|
+
let grpc_response = grpc_response.unwrap();
|
|
336
|
+
assert!(!grpc_response.metadata.is_empty());
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#[test]
|
|
340
|
+
fn test_invalid_metadata_key() {
|
|
341
|
+
let mut metadata = HashMap::new();
|
|
342
|
+
metadata.insert("invalid\nkey".to_string(), "value".to_string());
|
|
343
|
+
|
|
344
|
+
let response = RubyGrpcResponse {
|
|
345
|
+
payload: RefCell::new(b"test".to_vec()),
|
|
346
|
+
metadata: RefCell::new(metadata),
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
let result = response.into_grpc_response();
|
|
350
|
+
assert!(result.is_err());
|
|
351
|
+
}
|
|
352
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//! Ruby gRPC bindings for Spikard
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides a bridge between Ruby code and Spikard's gRPC runtime,
|
|
4
|
+
//! allowing Ruby handlers to process gRPC requests using protobuf serialization.
|
|
5
|
+
|
|
6
|
+
pub mod handler;
|
|
7
|
+
|
|
8
|
+
#[allow(unused_imports)]
|
|
9
|
+
pub use handler::{RubyGrpcHandler, RubyGrpcRequest, RubyGrpcResponse};
|
|
@@ -18,11 +18,13 @@
|
|
|
18
18
|
//! - `lifecycle`: Lifecycle hook implementations
|
|
19
19
|
//! - `sse`: Server-Sent Events support
|
|
20
20
|
//! - `websocket`: WebSocket support
|
|
21
|
+
//! - `grpc`: gRPC handler support
|
|
21
22
|
|
|
22
23
|
mod background;
|
|
23
24
|
mod config;
|
|
24
25
|
mod conversion;
|
|
25
26
|
mod di;
|
|
27
|
+
mod grpc;
|
|
26
28
|
mod gvl;
|
|
27
29
|
mod handler;
|
|
28
30
|
mod integration;
|
|
@@ -1645,12 +1647,14 @@ pub fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
1645
1647
|
let spikard_module = ruby.define_module("Spikard")?;
|
|
1646
1648
|
testing::websocket::init(ruby, &spikard_module)?;
|
|
1647
1649
|
testing::sse::init(ruby, &spikard_module)?;
|
|
1650
|
+
grpc::handler::init(ruby, &spikard_module)?;
|
|
1648
1651
|
|
|
1649
1652
|
let _ = NativeBuiltResponse::mark as fn(&NativeBuiltResponse, &Marker);
|
|
1650
1653
|
let _ = NativeLifecycleRegistry::mark as fn(&NativeLifecycleRegistry, &Marker);
|
|
1651
1654
|
let _ = NativeDependencyRegistry::mark as fn(&NativeDependencyRegistry, &Marker);
|
|
1652
1655
|
let _ = NativeRequest::mark as fn(&NativeRequest, &Marker);
|
|
1653
1656
|
let _ = RubyHandler::mark as fn(&RubyHandler, &Marker);
|
|
1657
|
+
let _ = grpc::handler::RubyGrpcHandler::mark as fn(&grpc::handler::RubyGrpcHandler, &Marker);
|
|
1654
1658
|
let _ = mark as fn(&NativeTestClient, &Marker);
|
|
1655
1659
|
|
|
1656
1660
|
Ok(())
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spikard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Na'aman Hirschfeld
|
|
@@ -71,6 +71,7 @@ files:
|
|
|
71
71
|
- lib/spikard/background.rb
|
|
72
72
|
- lib/spikard/config.rb
|
|
73
73
|
- lib/spikard/converters.rb
|
|
74
|
+
- lib/spikard/grpc.rb
|
|
74
75
|
- lib/spikard/handler_wrapper.rb
|
|
75
76
|
- lib/spikard/provide.rb
|
|
76
77
|
- lib/spikard/response.rb
|
|
@@ -90,6 +91,7 @@ files:
|
|
|
90
91
|
- vendor/crates/spikard-bindings-shared/src/conversion_traits.rs
|
|
91
92
|
- vendor/crates/spikard-bindings-shared/src/di_traits.rs
|
|
92
93
|
- vendor/crates/spikard-bindings-shared/src/error_response.rs
|
|
94
|
+
- vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs
|
|
93
95
|
- vendor/crates/spikard-bindings-shared/src/handler_base.rs
|
|
94
96
|
- vendor/crates/spikard-bindings-shared/src/lib.rs
|
|
95
97
|
- vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs
|
|
@@ -146,6 +148,10 @@ files:
|
|
|
146
148
|
- vendor/crates/spikard-http/src/cors.rs
|
|
147
149
|
- vendor/crates/spikard-http/src/debug.rs
|
|
148
150
|
- vendor/crates/spikard-http/src/di_handler.rs
|
|
151
|
+
- vendor/crates/spikard-http/src/grpc/handler.rs
|
|
152
|
+
- vendor/crates/spikard-http/src/grpc/mod.rs
|
|
153
|
+
- vendor/crates/spikard-http/src/grpc/service.rs
|
|
154
|
+
- vendor/crates/spikard-http/src/grpc/streaming.rs
|
|
149
155
|
- vendor/crates/spikard-http/src/handler_response.rs
|
|
150
156
|
- vendor/crates/spikard-http/src/handler_trait.rs
|
|
151
157
|
- vendor/crates/spikard-http/src/handler_trait_tests.rs
|
|
@@ -167,6 +173,7 @@ files:
|
|
|
167
173
|
- vendor/crates/spikard-http/src/openapi/spec_generation.rs
|
|
168
174
|
- vendor/crates/spikard-http/src/query_parser.rs
|
|
169
175
|
- vendor/crates/spikard-http/src/response.rs
|
|
176
|
+
- vendor/crates/spikard-http/src/server/grpc_routing.rs
|
|
170
177
|
- vendor/crates/spikard-http/src/server/handler.rs
|
|
171
178
|
- vendor/crates/spikard-http/src/server/lifecycle_execution.rs
|
|
172
179
|
- vendor/crates/spikard-http/src/server/mod.rs
|
|
@@ -180,12 +187,17 @@ files:
|
|
|
180
187
|
- vendor/crates/spikard-http/src/websocket.rs
|
|
181
188
|
- vendor/crates/spikard-http/tests/auth_integration.rs
|
|
182
189
|
- vendor/crates/spikard-http/tests/background_behavior.rs
|
|
190
|
+
- vendor/crates/spikard-http/tests/common/grpc_helpers.rs
|
|
183
191
|
- vendor/crates/spikard-http/tests/common/handlers.rs
|
|
184
192
|
- vendor/crates/spikard-http/tests/common/mod.rs
|
|
185
193
|
- vendor/crates/spikard-http/tests/common/test_builders.rs
|
|
186
194
|
- vendor/crates/spikard-http/tests/di_handler_error_responses.rs
|
|
187
195
|
- vendor/crates/spikard-http/tests/di_integration.rs
|
|
188
196
|
- vendor/crates/spikard-http/tests/doc_snippets.rs
|
|
197
|
+
- vendor/crates/spikard-http/tests/grpc_error_handling_test.rs
|
|
198
|
+
- vendor/crates/spikard-http/tests/grpc_integration_test.rs
|
|
199
|
+
- vendor/crates/spikard-http/tests/grpc_metadata_test.rs
|
|
200
|
+
- vendor/crates/spikard-http/tests/grpc_server_integration.rs
|
|
189
201
|
- vendor/crates/spikard-http/tests/lifecycle_execution.rs
|
|
190
202
|
- vendor/crates/spikard-http/tests/middleware_stack_integration.rs
|
|
191
203
|
- vendor/crates/spikard-http/tests/multipart_behavior.rs
|
|
@@ -221,6 +233,8 @@ files:
|
|
|
221
233
|
- vendor/crates/spikard-rb/src/conversion.rs
|
|
222
234
|
- vendor/crates/spikard-rb/src/di/builder.rs
|
|
223
235
|
- vendor/crates/spikard-rb/src/di/mod.rs
|
|
236
|
+
- vendor/crates/spikard-rb/src/grpc/handler.rs
|
|
237
|
+
- vendor/crates/spikard-rb/src/grpc/mod.rs
|
|
224
238
|
- vendor/crates/spikard-rb/src/gvl.rs
|
|
225
239
|
- vendor/crates/spikard-rb/src/handler.rs
|
|
226
240
|
- vendor/crates/spikard-rb/src/integration/mod.rs
|