trusted 0.2.1 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 15cfbc475e286b40d3e94ded11c1110cdf10f952
4
- data.tar.gz: 60bd0659b8d3cd8fe5051aee181faeecf11965df
3
+ metadata.gz: fe22de0a36b2b3bc06d99ce763da9541fa65adc8
4
+ data.tar.gz: 2014461c82af74382935959786d7fb059de8a82c
5
5
  SHA512:
6
- metadata.gz: 52a189eec45ad712297f14f3db1a5f271f7eefe8a5555f456df635412bd153b66706ddc397c19a567c0df3195ad8012918cac09d949c39a01d8dcb5aa1ca058c
7
- data.tar.gz: 1a78a573c140d88142932128aee279b2ba9c501592e66ad735b273a88211601d8cd77397fb3ac1dbcdf2fb4db9db776feaa19a9ea9c5bebcceb69c2726902a1e
6
+ metadata.gz: 8b5217afe22e37f29b4967f3a0a42309fc57a22b53171a054fb3afaf2eb2fd18afd65542f7f34a38202f30af4d068af5d9078d3e050c741bb0e5f994716a2cbb
7
+ data.tar.gz: 9b7b02314578c6be323863b20b7841501e9a89638fccf8c24a2b2d04d1331a8a27d6ff57101bf7f813f2bf43e496e81522ee514a9d9e821ae5aec06a5beea855
data/bin/trusted ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'optparse'
5
+
6
+ require 'trusted'
7
+
8
+ options = {}
9
+
10
+ opt_parser = OptionParser.new do |opts|
11
+ opts.banner = "Usage: trusted [options]"
12
+
13
+ opts.on("-c path", "--config PATH", "Load configuration file") do |config|
14
+ contents = File.read(config)
15
+
16
+ # TODO: Move to Builder and use cleanroom instead of eval
17
+ options[:config] = Trusted::Config::Builder.dsl { eval contents }
18
+ end
19
+
20
+ opts.on("-r path", "--rackup PATH", "Load rackup file") do |rackup|
21
+ options[:rackup] = rackup
22
+ end
23
+ end
24
+
25
+ opt_parser.parse!
26
+
27
+ config = options[:config] || ::Trusted::Config::Builder.new.build
28
+ rackup = options[:rackup] || 'config.ru'
29
+
30
+ app, _ = Rack::Builder.parse_file(rackup)
31
+
32
+ Rack::Handler::Trusted.run(app, trusted_config: config)
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "trusted"
3
- version = "0.2.1"
3
+ version = "0.3.2"
4
4
  authors = ["Dmitry Gritsay <dmitry@vinted.com>"]
5
5
 
6
6
  [lib]
@@ -10,4 +10,4 @@ crate-type = ["dylib"]
10
10
  hyper = "0.9.10"
11
11
  hyperlocal = { git = "https://github.com/softprops/hyperlocal.git" }
12
12
  lazy_static = "0.2.1"
13
- ruru = "0.8.1"
13
+ ruru = "0.9.2"
@@ -0,0 +1,34 @@
1
+ use std::convert::From;
2
+
3
+ #[derive(Debug, PartialEq)]
4
+ pub enum BindingType {
5
+ Tcp,
6
+ Unix,
7
+ }
8
+
9
+ impl<'a> From<&'a str> for BindingType {
10
+ fn from(string: &str) -> Self {
11
+ match string {
12
+ "tcp" => BindingType::Tcp,
13
+ "unix" => BindingType::Unix,
14
+
15
+ // TODO: move to `Result` and return `Err`
16
+ _ => panic!("Unsupported binding type"),
17
+ }
18
+ }
19
+ }
20
+
21
+ #[cfg(test)]
22
+ mod tests {
23
+ use super::*;
24
+
25
+ #[test]
26
+ fn it_is_converted_from_tcp() {
27
+ assert_eq!(BindingType::from("tcp"), BindingType::Tcp);
28
+ }
29
+
30
+ #[test]
31
+ fn it_is_converted_from_unix() {
32
+ assert_eq!(BindingType::from("unix"), BindingType::Unix);
33
+ }
34
+ }
@@ -0,0 +1,66 @@
1
+ use ruru::{Fixnum, Object, RString, Symbol};
2
+
3
+ use ruby::Config as RubyConfig;
4
+
5
+ use super::BindingType;
6
+
7
+ pub struct Config {
8
+ binding_type: BindingType,
9
+ listen_on: String,
10
+ thread_pool_size: usize,
11
+ }
12
+
13
+ impl Config {
14
+ pub fn new(binding_type: BindingType, listen_on: String, thread_pool_size: usize) -> Self {
15
+ Config {
16
+ binding_type: binding_type,
17
+ listen_on: listen_on,
18
+ thread_pool_size: thread_pool_size,
19
+ }
20
+ }
21
+
22
+ #[inline]
23
+ pub fn binding_type(&self) -> &BindingType {
24
+ &self.binding_type
25
+ }
26
+
27
+ #[inline]
28
+ pub fn listen_on(&self) -> &str {
29
+ &self.listen_on
30
+ }
31
+
32
+ #[inline]
33
+ pub fn thread_pool_size(&self) -> usize {
34
+ self.thread_pool_size
35
+ }
36
+ }
37
+
38
+
39
+ impl From<RubyConfig> for Config {
40
+ fn from(ruby_config: RubyConfig) -> Self {
41
+ // TODO: raise an exception if it is not a String
42
+ let listen_on =
43
+ ruby_config
44
+ .send("listen_on", vec![])
45
+ .try_convert_to::<RString>().unwrap()
46
+ .to_string();
47
+
48
+ // TODO: raise an exception if it is not a Symbol
49
+ let binding_type =
50
+ ruby_config
51
+ .send("binding_type", vec![])
52
+ .try_convert_to::<Symbol>().unwrap()
53
+ .to_string();
54
+
55
+ let binding_type = BindingType::from(binding_type.as_str());
56
+
57
+ // TODO: raise an exception if it is not a Symbol
58
+ let thread_pool_size =
59
+ ruby_config
60
+ .send("thread_pool_size", vec![])
61
+ .try_convert_to::<Fixnum>().unwrap()
62
+ .to_i64() as usize;
63
+
64
+ Config::new(binding_type, listen_on, thread_pool_size)
65
+ }
66
+ }
@@ -0,0 +1,5 @@
1
+ mod binding_type;
2
+ mod config;
3
+
4
+ pub use self::binding_type::BindingType;
5
+ pub use self::config::Config;
@@ -0,0 +1,25 @@
1
+ use std::sync::mpsc::{Receiver, Sender};
2
+
3
+ pub struct Channel<S, R> {
4
+ sender: Sender<S>,
5
+ receiver: Receiver<R>,
6
+ }
7
+
8
+ impl<S, R> Channel<S, R> {
9
+ pub fn new(sender: Sender<S>, receiver: Receiver<R>) -> Self {
10
+ Channel {
11
+ sender: sender,
12
+ receiver: receiver,
13
+ }
14
+ }
15
+
16
+ #[inline]
17
+ pub fn sender(&self) -> &Sender<S> {
18
+ &self.sender
19
+ }
20
+
21
+ #[inline]
22
+ pub fn receiver(&self) -> &Receiver<R> {
23
+ &self.receiver
24
+ }
25
+ }
@@ -0,0 +1,77 @@
1
+ use std::io::Read;
2
+ use std::os::unix::io::AsRawFd;
3
+ use std::os::unix::net::UnixStream;
4
+ use std::sync::mpsc::{self, Receiver, Sender};
5
+
6
+ use ruru::{Class, Fixnum, Object, Proc, Thread};
7
+
8
+ use core::{Channel, ResponseFuture};
9
+ use request_processor::RequestProcessor;
10
+ use request::Request;
11
+ use response::Response;
12
+
13
+ use ruby::request::ProcessingPool;
14
+
15
+ pub struct Core {
16
+ channel: Channel<ResponseFuture, Request>,
17
+ ruby_handler: Proc,
18
+ fake_stream: UnixStream,
19
+ thread_pool_size: usize,
20
+ }
21
+
22
+ impl Core {
23
+ pub fn new(sender: Sender<ResponseFuture>,
24
+ receiver: Receiver<Request>,
25
+ ruby_handler: Proc,
26
+ fake_stream: UnixStream,
27
+ thread_pool_size: usize) -> Self {
28
+
29
+ Core {
30
+ channel: Channel::new(sender, receiver),
31
+ ruby_handler: ruby_handler,
32
+ fake_stream: fake_stream,
33
+ thread_pool_size: thread_pool_size,
34
+ }
35
+ }
36
+
37
+ pub fn run(&mut self) {
38
+ let args = vec![
39
+ self.ruby_handler.to_any_object(),
40
+ Fixnum::new(self.thread_pool_size as i64).to_any_object(),
41
+ ];
42
+
43
+ let processing_pool = Class::from_existing("Trusted")
44
+ .get_nested_class("Request")
45
+ .get_nested_class("ProcessingPool")
46
+ .new_instance(args);
47
+
48
+ // Can be casted unsafely, because processing pool is created directly from
49
+ // ProcessingPool class
50
+ let processing_pool = unsafe { processing_pool.to::<ProcessingPool>() };
51
+
52
+ // Buffer for reading a byte from handlers when a fake socket is ready for reading
53
+ let mut buffer = [0u8; 1];
54
+
55
+ let fd = self.fake_stream.as_raw_fd();
56
+
57
+ loop {
58
+ println!("[rust] Waiting for request");
59
+
60
+ // Handler will send one byte when new request is ready for processing
61
+ // Thread::wait_fd() allows other Ruby threads to be scheduled while Core
62
+ // is waiting for new requests
63
+ Thread::wait_fd(fd);
64
+
65
+ self.fake_stream.read(&mut buffer).unwrap();
66
+
67
+ let request = self.channel.receiver().recv().unwrap();
68
+ let (response_sender, response_receiver) = mpsc::channel::<Response>();
69
+
70
+ self.channel.sender().send(response_receiver).unwrap();
71
+
72
+ let observer = ::ruby::request::Observer::new(response_sender);
73
+
74
+ RequestProcessor::new(request, &processing_pool).handle(observer);
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,73 @@
1
+ use std::io::Write;
2
+ use std::os::unix::net::UnixStream;
3
+ use std::sync::Mutex;
4
+ use std::sync::mpsc::{Sender, Receiver};
5
+
6
+ use hyper::server::{Handler as HyperHandler, Request as HyperRequest, Response as HyperResponse};
7
+ use hyper::status::StatusCode;
8
+
9
+ use core::{Channel, ResponseFuture};
10
+ use request::Request;
11
+
12
+ pub struct Handler {
13
+ channel: Mutex<Channel<Request, ResponseFuture>>,
14
+ stream: Mutex<UnixStream>,
15
+ }
16
+
17
+ impl Handler {
18
+ pub fn new(sender: Sender<Request>,
19
+ receiver: Receiver<ResponseFuture>,
20
+ stream: UnixStream) -> Self {
21
+
22
+ let channel = Channel::new(sender, receiver);
23
+
24
+ Handler {
25
+ channel: Mutex::new(channel),
26
+ stream: Mutex::new(stream),
27
+ }
28
+ }
29
+
30
+ fn response_future(&self, request: Request) -> ResponseFuture {
31
+ let channel = self.channel.lock().unwrap();
32
+
33
+ // Tell MRI to switch to main thread by sending data using file descriptor
34
+ {
35
+ let buffer = [0u8; 1];
36
+
37
+ self.stream.lock().unwrap().write(&buffer).unwrap();
38
+ }
39
+
40
+ channel.sender().send(request).unwrap();
41
+
42
+ // Receive a `ResponseFuture`
43
+ channel.receiver().recv().unwrap()
44
+ }
45
+ }
46
+
47
+ impl HyperHandler for Handler {
48
+ fn handle(&self, hyper_request: HyperRequest, mut hyper_response: HyperResponse) {
49
+ println!("[hyper] New request received");
50
+
51
+ let request = hyper_request.into();
52
+ let response_future = self.response_future(request);
53
+
54
+ // Receive a response
55
+ let response = response_future.recv().unwrap();
56
+
57
+ {
58
+ let headers = hyper_response.headers_mut();
59
+ *headers = response.headers;
60
+ }
61
+
62
+ {
63
+ let status = hyper_response.status_mut();
64
+ *status = StatusCode::from_u16(response.status);
65
+ }
66
+
67
+ let mut res = hyper_response.start().unwrap();
68
+ res.write_all(response.body.as_bytes()).unwrap();
69
+
70
+ println!("[hyper] Response successfully sent to client");
71
+ println!("[hyper] ------------------------------------");
72
+ }
73
+ }
@@ -0,0 +1,15 @@
1
+ mod channel;
2
+ mod core;
3
+ mod handler;
4
+ mod server;
5
+
6
+ use std::sync::mpsc::Receiver;
7
+
8
+ use response::Response;
9
+
10
+ pub use self::channel::Channel;
11
+ pub use self::core::Core;
12
+ pub use self::handler::Handler;
13
+ pub use self::server::Server;
14
+
15
+ pub type ResponseFuture = Receiver<Response>;
@@ -0,0 +1,70 @@
1
+ use std::os::unix::net::UnixStream;
2
+ use std::sync::mpsc;
3
+ use std::thread;
4
+
5
+ use hyper::server::Server as HyperServer;
6
+ use hyperlocal::UnixSocketServer;
7
+ use ruru::{Proc, VM};
8
+
9
+ use config::{BindingType, Config};
10
+ use core::{Core, Handler, ResponseFuture};
11
+ use request::Request;
12
+
13
+ pub struct Server {
14
+ config: Config,
15
+ }
16
+
17
+ impl Server {
18
+ pub fn new(config: Config) -> Self {
19
+ Server { config: config }
20
+ }
21
+
22
+ pub fn listen(self, ruby_handler: Proc) {
23
+ println!("[rust] Start listening");
24
+
25
+ let (handler_stream, core_stream) = UnixStream::pair().unwrap();
26
+
27
+ let (request_sender, request_receiver) = mpsc::channel::<Request>();
28
+ let (response_future_sender, response_future_receiver) = mpsc::channel::<ResponseFuture>();
29
+
30
+ let handler = Handler::new(request_sender, response_future_receiver, handler_stream);
31
+
32
+ let mut core = Core::new(response_future_sender,
33
+ request_receiver,
34
+ ruby_handler,
35
+ core_stream,
36
+ self.config.thread_pool_size());
37
+
38
+ thread::spawn(move || {
39
+ let handler_function = || -> () {
40
+ println!("[hyper] GVL released for server thread");
41
+
42
+ let thread_pool_size = self.config.thread_pool_size();
43
+
44
+ println!("[hyper] Spawning {} native thread(s)", thread_pool_size);
45
+
46
+ match *self.config.binding_type() {
47
+ BindingType::Unix => {
48
+ UnixSocketServer::new(self.config.listen_on()).unwrap()
49
+ .handle_threads(handler, thread_pool_size).unwrap();
50
+ },
51
+ BindingType::Tcp => {
52
+ HyperServer::http(self.config.listen_on()).unwrap()
53
+ .handle_threads(handler, thread_pool_size).unwrap();
54
+ }
55
+ };
56
+ };
57
+
58
+ let unblock_function = || {
59
+ ()
60
+ };
61
+
62
+ VM::thread_call_without_gvl(
63
+ handler_function,
64
+ Some(unblock_function)
65
+ );
66
+ });
67
+
68
+ core.run();
69
+ }
70
+ }
@@ -4,16 +4,20 @@
4
4
  extern crate hyper;
5
5
  extern crate hyperlocal;
6
6
 
7
- mod handler;
7
+ mod config;
8
+ mod core;
8
9
  mod request;
9
10
  mod request_processor;
10
11
  mod response;
11
12
  mod ruby;
12
- mod server;
13
13
 
14
- use ruby::Server;
14
+ use ruby::{Request, Response, Server};
15
+ use ruby::request::Observer;
15
16
 
16
17
  #[no_mangle]
17
18
  pub extern fn initialize_my_app() {
19
+ Observer::define_ruby_class();
20
+ Request::define_ruby_class();
21
+ Response::define_ruby_class();
18
22
  Server::define_ruby_class();
19
23
  }
@@ -5,16 +5,81 @@ use hyper::header::Headers;
5
5
  use hyper::server::{Request as HyperRequest};
6
6
 
7
7
  pub struct Request {
8
- pub method: String,
9
- pub url: String,
10
- pub path_info: String,
11
- pub query_string: String,
12
- pub remote_addr: String,
13
- pub server_port: String,
14
- pub headers: Headers,
15
- pub body: String,
8
+ method: String,
9
+ url: String,
10
+ path_info: String,
11
+ query_string: String,
12
+ remote_addr: String,
13
+ server_port: String,
14
+ headers: Headers,
15
+ body: String,
16
16
  }
17
17
 
18
+ impl Request {
19
+ pub fn new(method: String,
20
+ url: String,
21
+ path_info: String,
22
+ query_string: String,
23
+ remote_addr: String,
24
+ server_port: String,
25
+ headers: Headers,
26
+ body: String)
27
+ -> Self {
28
+ Request {
29
+ method: method,
30
+ url: url,
31
+ path_info: path_info,
32
+ query_string: query_string,
33
+ remote_addr: remote_addr,
34
+ server_port: server_port,
35
+ headers: headers,
36
+ body: body,
37
+ }
38
+ }
39
+
40
+ #[inline]
41
+ pub fn method(&self) -> &str {
42
+ &self.method
43
+ }
44
+
45
+ #[inline]
46
+ pub fn url(&self) -> &str {
47
+ &self.url
48
+ }
49
+
50
+ #[inline]
51
+ pub fn path_info(&self) -> &str {
52
+ &self.path_info
53
+ }
54
+
55
+ #[inline]
56
+ pub fn query_string(&self) -> &str {
57
+ &self.query_string
58
+ }
59
+
60
+ #[inline]
61
+ pub fn remote_addr(&self) -> &str {
62
+ &self.remote_addr
63
+ }
64
+
65
+ #[inline]
66
+ pub fn server_port(&self) -> &str {
67
+ &self.server_port
68
+ }
69
+
70
+ #[inline]
71
+ pub fn headers(&self) -> &Headers {
72
+ &self.headers
73
+ }
74
+
75
+ #[inline]
76
+ pub fn body(&self) -> &str {
77
+ &self.body
78
+ }
79
+ }
80
+
81
+ wrappable_struct!(Request, RequestWrapper, REQUEST_DATA_TYPE);
82
+
18
83
  impl<'a, 'b> From<HyperRequest<'a, 'b>> for Request {
19
84
  fn from(mut request: HyperRequest) -> Self {
20
85
  let mut body = String::new();
@@ -32,15 +97,13 @@ impl<'a, 'b> From<HyperRequest<'a, 'b>> for Request {
32
97
  let remote_addr = request.remote_addr.ip().to_string();
33
98
  let server_port = request.remote_addr.port().to_string();
34
99
 
35
- Request {
36
- method: method,
37
- url: url,
38
- path_info: path_info,
39
- query_string: query_string,
40
- remote_addr: remote_addr,
41
- server_port: server_port,
42
- headers: request.headers,
43
- body: body,
44
- }
100
+ Request::new(method,
101
+ url,
102
+ path_info,
103
+ query_string,
104
+ remote_addr,
105
+ server_port,
106
+ request.headers,
107
+ body)
45
108
  }
46
109
  }
@@ -1,28 +1,24 @@
1
- use ruru::{Object, Proc};
2
-
3
1
  use request::Request;
4
- use response::Response;
5
2
  use ruby::{Request as RubyRequest, Response as RubyResponse};
3
+ use ruby::request::{Observer, ProcessingPool};
6
4
 
7
5
  pub struct RequestProcessor<'a> {
8
6
  request: Request,
9
- ruby_handler: &'a Proc,
7
+ processing_pool: &'a ProcessingPool,
10
8
  }
11
9
 
12
10
  impl<'a> RequestProcessor<'a> {
13
- pub fn new(request: Request, ruby_handler: &'a Proc) -> Self {
11
+ pub fn new(request: Request, processing_pool: &'a ProcessingPool) -> Self {
14
12
  RequestProcessor {
15
13
  request: request,
16
- ruby_handler: ruby_handler,
14
+ processing_pool: processing_pool,
17
15
  }
18
16
  }
19
17
 
20
- pub fn handle(self) -> Response {
18
+ pub fn handle(self, observer: Observer) {
21
19
  let ruby_request = RubyRequest::from(self.request);
22
20
  let ruby_response = RubyResponse::new();
23
21
 
24
- self.ruby_handler.call(vec![ruby_request.to_any_object(), ruby_response.to_any_object()]);
25
-
26
- ruby_response.into()
22
+ self.processing_pool.process(ruby_request, ruby_response, observer);
27
23
  }
28
24
  }
@@ -0,0 +1,13 @@
1
+ use ruru::{Object, VerifiedObject};
2
+
3
+ class!(Config);
4
+
5
+ impl VerifiedObject for Config {
6
+ fn is_correct_type<T: Object>(object: &T) -> bool {
7
+ object.respond_to("binding_type") && object.respond_to("listen_on")
8
+ }
9
+
10
+ fn error_message() -> &'static str {
11
+ "Error converting to Config"
12
+ }
13
+ }
@@ -1,7 +1,10 @@
1
- mod request;
1
+ mod config;
2
2
  mod response;
3
3
  mod server;
4
4
 
5
- pub use self::request::Request;
5
+ pub mod request;
6
+
7
+ pub use self::config::Config;
6
8
  pub use self::response::Response;
7
9
  pub use self::server::Server;
10
+ pub use self::request::Request;
@@ -0,0 +1,7 @@
1
+ mod observer;
2
+ mod processing_pool;
3
+ mod request;
4
+
5
+ pub use self::observer::Observer;
6
+ pub use self::processing_pool::ProcessingPool;
7
+ pub use self::request::Request;