├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.rst ├── examples ├── fetch.rs ├── monitor.rs └── serve.rs ├── src ├── accept.rs ├── errors.rs ├── extensions.rs ├── intention.rs ├── lib.rs ├── persistent.rs ├── protocol.rs ├── stream.rs ├── substr.rs ├── trait_impls.rs └── transport.rs └── vagga.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /.vagga 2 | /Cargo.lock 3 | /target 4 | /tmp 5 | 6 | 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | sudo: false 7 | env: 8 | secure: "e7tLN2ucVjZXPaA7egf0zSd8ZxMEL8RFIT9Og53XeryPmp4kthFC6f12w+HcockJHfZoXFYqryu7uNw91AXz/y/04zjT6yWR8h0ei1o0AHpduKpf5xLyj94kBZBEBq/9B2gqG1YeCtO34IwvzZqufqNR3LWuRf27Wy2bRVexcHjnxhfQ+vop09jehqHWAyN9uqVMH5Ov38c+X6EeKvk0NYP4cpE8BC/CPWto2653zONizqOIg6oGtfYF30tMjMOeLkOyVUxb5K0QSLLAkyFPYTwIIaSHH4ErmoXtMO7DCygB6yhI5qhylFV3jZSCXJBaKW+gycj8OiSrZ7xvjvoYiZ6tafoqn5+wG48gLljid1LY6SbdXw0KMbR9JPSyZ/Nm8TOm6pdokThQ9A7oZPwIGOM1HHAkiV/1+sXNbOWrF1yBgWLmpucQgQ1bjKblQqFIngmxflONmnT+lrMQfO0iReuvEjo2EyvTSp4hqX/2ur06G4YfqGyau1psHXL+QVhWRussvjMMYN6Bh3sbiJuFIbVe6NaYwWdvA1rfeBe1P/wbWkakwMVqe350blIIJ2TgfsYyXrQ18f2eqHNOXIZkYyrUFVHSgOriW6Z9w1F/kiECI5xeW7AwYbfPIkPUFZchnvCkAdV+/VLzPIVufk07RtIYngy/H84H/Lpqt3P069c=" 9 | script: 10 | - cargo build --verbose 11 | - cargo test --verbose 12 | after_success: | 13 | [ $TRAVIS_RUST_VERSION = stable ] && 14 | [ $TRAVIS_BRANCH = master ] && 15 | [ $TRAVIS_PULL_REQUEST = false ] && 16 | cargo doc && 17 | echo "" > target/doc/index.html && 18 | pip install ghp-import --user && 19 | ~/.local/bin/ghp-import -n target/doc && 20 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 21 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rotor-stream" 3 | description = """ 4 | The stream abstration on top of rotor. This is the easiest way to build a 5 | protocol on top of TCP (Unix sockets) with rotor 6 | """ 7 | license = "MIT" 8 | readme = "README.rst" 9 | keywords = ["stream", "rotor", "tcp", "sockets", "mio"] 10 | homepage = "http://github.com/tailhook/rotor-stream" 11 | version = "0.6.2" 12 | authors = ["paul@colomiets.name"] 13 | 14 | [dependencies] 15 | rotor = "0.6.1" 16 | rotor-tools = { optional = true, version = "0.3.1"} 17 | netbuf = "0.3.1" 18 | memchr = "0.1.7" 19 | quick-error = "0.2.1" 20 | log = "0.3.5" 21 | 22 | [dev-dependencies] 23 | argparse = "0.2.1" 24 | nix = "0.4.2" 25 | httparse = "1.1.0" 26 | env_logger = "0.3.2" 27 | 28 | [features] 29 | default = ["replaceable"] 30 | replaceable = ["rotor-tools"] 31 | 32 | [lib] 33 | name = "rotor_stream" 34 | path = "src/lib.rs" 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Paul Colomiets 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Rotor Stream 3 | ============ 4 | 5 | :Status: Alpha 6 | :Dependencies: rotor_, mio_, netbuf_ 7 | 8 | .. _rotor: http://github.com/tailhook/rotor 9 | .. _mio: https://github.com/carllerche/mio 10 | .. _netbuf: https://github.com/tailhook/netbuf 11 | 12 | A stream abstraction based on MIO. Features: 13 | 14 | * State machine-based implementation (as usually in rotor_) 15 | * Uses netbuf_ for buffering, buffer has contiguous data slice (easy parsing) 16 | * Input data abstractions: read-x-bytes, read-until-delimiter 17 | * Perfect for request-reply style protocols 18 | * Independent of whether it's client or server, tcp or unix sockets 19 | * (TODO) should work on top of SSL later 20 | -------------------------------------------------------------------------------- /examples/fetch.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | extern crate nix; 4 | extern crate rotor; 5 | extern crate rotor_stream; 6 | extern crate argparse; 7 | extern crate httparse; 8 | 9 | use std::cmp::max; 10 | use std::str::from_utf8; 11 | use std::net::ToSocketAddrs; 12 | use std::io::{stdout, stderr, Write}; 13 | use std::time::Duration; 14 | use std::error::Error; 15 | 16 | use argparse::{ArgumentParser, Store}; 17 | use rotor::mio::tcp::{TcpStream}; 18 | use rotor_stream::{Stream, Transport, Protocol, Intent, Exception}; 19 | use rotor::{Scope}; 20 | 21 | 22 | struct Context; 23 | 24 | enum Http { 25 | SendRequest(String), 26 | ReadHeaders, 27 | ReadBody, 28 | } 29 | 30 | 31 | impl<'a> Protocol for Http { 32 | type Context = Context; 33 | type Socket = TcpStream; 34 | type Seed = (String, String); 35 | fn create((host, path): Self::Seed, _sock: &mut TcpStream, 36 | scope: &mut Scope) 37 | -> Intent 38 | { 39 | // Wait socket to become writable 40 | let req = format!(concat!( 41 | "GET {} HTTP/1.1\r\n", 42 | "Host: {}\r\n", 43 | "\r\n"), 44 | path, host); 45 | println!("----- Request -----"); 46 | print!("{}", req); 47 | Intent::of(Http::SendRequest(req)).expect_flush() 48 | .deadline(scope.now() + Duration::new(10, 0)) 49 | } 50 | fn bytes_flushed(self, transport: &mut Transport, 51 | scope: &mut Scope) 52 | -> Intent 53 | { 54 | match self { 55 | Http::SendRequest(val) => { 56 | transport.output().extend(val.as_bytes()); 57 | Intent::of(Http::ReadHeaders) 58 | .expect_delimiter(b"\r\n\r\n", 4096) 59 | .deadline(scope.now() + Duration::new(10, 0)) 60 | } 61 | _ => unreachable!(), 62 | } 63 | } 64 | fn bytes_read(self, transport: &mut Transport, 65 | end: usize, scope: &mut Scope) 66 | -> Intent 67 | { 68 | match self { 69 | Http::SendRequest(_) => unreachable!(), 70 | Http::ReadHeaders => { 71 | let mut headers = [httparse::EMPTY_HEADER; 16]; 72 | { 73 | let mut req = httparse::Response::new(&mut headers); 74 | let buf = &transport.input()[..end+4]; 75 | req.parse(buf).unwrap(); 76 | } 77 | let mut clen = 0; 78 | println!("----- Headers -----"); 79 | for header in headers.iter() { 80 | println!("{}: {}", 81 | header.name, 82 | from_utf8(header.value).unwrap()); 83 | let hlower = header.name.to_lowercase(); 84 | if &hlower[..] == "content-length" { 85 | if let Some(val) = from_utf8(header.value).ok() 86 | .and_then(|x| x.parse().ok()) { 87 | clen = val; 88 | break; 89 | } 90 | } 91 | } 92 | println!("----- Body [{}] -----", clen); 93 | transport.input().consume(end + 4); 94 | Intent::of(Http::ReadBody).expect_bytes(clen) 95 | .deadline(scope.now() + 96 | Duration::new(max(10, clen as u64), 0)) 97 | } 98 | Http::ReadBody => { 99 | let newline = { 100 | let data = &transport.input()[..]; 101 | data.len() > 0 && &data[data.len()-1..] != b"\n" 102 | }; 103 | transport.input().write_to(&mut stdout()).ok(); 104 | if newline { 105 | println!(""); 106 | } 107 | println!("----- Done -----"); 108 | Intent::done() 109 | } 110 | } 111 | } 112 | fn timeout(self, _transport: &mut Transport, 113 | _scope: &mut Scope) 114 | -> Intent 115 | { 116 | writeln!(&mut stderr(), "Timeout reached").ok(); 117 | Intent::done() 118 | } 119 | 120 | /// Message received (from the main loop) 121 | fn wakeup(self, _transport: &mut Transport, 122 | _scope: &mut Scope) 123 | -> Intent 124 | { 125 | unreachable!(); 126 | } 127 | 128 | fn exception(self, _transport: &mut Transport, 129 | reason: Exception, scope: &mut Scope) 130 | -> Intent 131 | { 132 | writeln!(&mut stderr(), "Error when fetching data: {}", reason).ok(); 133 | scope.shutdown_loop(); 134 | Intent::done() 135 | } 136 | fn fatal(self, reason: Exception, scope: &mut Scope) 137 | -> Option> 138 | { 139 | writeln!(&mut stderr(), "Error when fetching data: {}", reason).ok(); 140 | scope.shutdown_loop(); 141 | None 142 | } 143 | } 144 | 145 | fn main() { 146 | let mut url = "".to_string(); 147 | { 148 | let mut ap = ArgumentParser::new(); 149 | ap.set_description(" 150 | Fetches some url. Only http, and only fixed size pages are 151 | supported (i.e. where Content-Length is set) 152 | "); 153 | ap.refer(&mut url).add_argument("url", Store, " 154 | The url to fetch."); 155 | ap.parse_args_or_exit(); 156 | } 157 | if !url.starts_with("http://") { 158 | println!("Url should start with http://"); 159 | exit(1); 160 | } 161 | let (host, path) = if let Some(idx) = url[7..].find('/') { 162 | (&url[7..idx+7], &url[idx+7..]) 163 | } else { 164 | (&url[7..], "/") 165 | }; 166 | println!("Host: {} (port: 80), path: {}", host, path); 167 | 168 | let event_loop = rotor::Loop::new(&rotor::Config::new()).unwrap(); 169 | 170 | let sock = TcpStream::connect( 171 | // Any better way for current stable rust? 172 | &(host, 80).to_socket_addrs().unwrap().next().unwrap()).unwrap(); 173 | 174 | let mut loop_inst = event_loop.instantiate(Context); 175 | loop_inst.add_machine_with(|scope| { 176 | Stream::::new( 177 | sock, (host.to_string(), path.to_string()), 178 | scope) 179 | }).unwrap(); 180 | loop_inst.run().unwrap(); 181 | } 182 | -------------------------------------------------------------------------------- /examples/monitor.rs: -------------------------------------------------------------------------------- 1 | extern crate nix; 2 | extern crate rotor; 3 | extern crate rotor_stream; 4 | extern crate argparse; 5 | extern crate httparse; 6 | extern crate env_logger; 7 | 8 | use std::cmp::max; 9 | use std::str::from_utf8; 10 | use std::net::ToSocketAddrs; 11 | use std::io::{stdout, stderr, Write}; 12 | use std::time::Duration; 13 | use std::error::Error; 14 | 15 | use rotor::mio::tcp::{TcpStream}; 16 | use rotor_stream::{Persistent, Transport, Protocol, Intent, Exception}; 17 | use rotor::{Scope}; 18 | 19 | 20 | struct Context; 21 | 22 | #[derive(Debug)] 23 | enum Http { 24 | SendRequest(String), 25 | ReadHeaders(String), 26 | ReadBody(String), 27 | Sleep(String), 28 | } 29 | 30 | 31 | impl<'a> Protocol for Http { 32 | type Context = Context; 33 | type Socket = TcpStream; 34 | type Seed = (String, String); 35 | fn create((host, path): Self::Seed, _sock: &mut TcpStream, 36 | scope: &mut Scope) 37 | -> Intent 38 | { 39 | println!("create"); 40 | // Wait socket to become writable 41 | let req = format!(concat!( 42 | "GET {} HTTP/1.1\r\n", 43 | "Host: {}\r\n", 44 | "User-Agent: curl\r\n", // required for timeapi.org 45 | "\r\n"), 46 | path, host); 47 | Intent::of(Http::SendRequest(req)).expect_flush() 48 | .deadline(scope.now() + Duration::new(10, 0)) 49 | } 50 | fn bytes_flushed(self, transport: &mut Transport, 51 | scope: &mut Scope) 52 | -> Intent 53 | { 54 | println!("bytes_flushed {:?}", self); 55 | match self { 56 | Http::SendRequest(s) => { 57 | transport.output().extend(s.as_bytes()); 58 | Intent::of(Http::ReadHeaders(s)) 59 | .expect_delimiter(b"\r\n\r\n", 4096) 60 | .deadline(scope.now() + Duration::new(10, 0)) 61 | } 62 | _ => unreachable!(), 63 | } 64 | } 65 | fn bytes_read(self, transport: &mut Transport, 66 | end: usize, scope: &mut Scope) 67 | -> Intent 68 | { 69 | println!("bytes_read {:?}", self); 70 | match self { 71 | Http::Sleep(x) => Intent::of(Http::Sleep(x)).sleep() 72 | // Imprecise, still good enough 73 | .deadline(scope.now() + Duration::new(5, 0)), 74 | Http::SendRequest(_) => unreachable!(), 75 | Http::ReadHeaders(s) => { 76 | let mut headers = [httparse::EMPTY_HEADER; 16]; 77 | { 78 | let mut req = httparse::Response::new(&mut headers); 79 | let buf = &transport.input()[..end+4]; 80 | req.parse(buf).unwrap(); 81 | } 82 | let mut clen = 0; 83 | for header in headers.iter() { 84 | let hlower = header.name.to_lowercase(); 85 | if &hlower[..] == "content-length" { 86 | if let Some(val) = from_utf8(header.value).ok() 87 | .and_then(|x| x.parse().ok()) { 88 | clen = val; 89 | break; 90 | } 91 | } 92 | } 93 | transport.input().consume(end + 4); 94 | Intent::of(Http::ReadBody(s)).expect_bytes(clen) 95 | .deadline(scope.now() + Duration::new( 96 | max(10, clen as u64), 0)) 97 | } 98 | Http::ReadBody(s) => { 99 | let newline = { 100 | let data = &transport.input()[..]; 101 | data.len() > 0 && &data[data.len()-1..] != b"\n" 102 | }; 103 | transport.input().write_to(&mut stdout()).ok(); 104 | if newline { 105 | println!(""); 106 | } 107 | println!("at {:?} wait for {:?} ", 108 | scope.now(), scope.now() + Duration::new(10, 0)); 109 | Intent::of(Http::Sleep(s)).sleep() 110 | .deadline(scope.now() + Duration::new(10, 0)) 111 | } 112 | } 113 | } 114 | fn timeout(self, _transport: &mut Transport, 115 | scope: &mut Scope) 116 | -> Intent 117 | { 118 | println!("timeout {:?} at {:?}", self, scope.now()); 119 | match self { 120 | Http::Sleep(x) => { 121 | Intent::of(Http::SendRequest(x)).expect_flush() 122 | .deadline(scope.now() + Duration::new(10, 0)) 123 | } 124 | _ => { 125 | writeln!(&mut stderr(), "Timeout reached").ok(); 126 | Intent::done() 127 | } 128 | } 129 | } 130 | 131 | /// Message received (from the main loop) 132 | fn wakeup(self, _transport: &mut Transport, 133 | _scope: &mut Scope) 134 | -> Intent 135 | { 136 | unreachable!("wakeup"); 137 | } 138 | 139 | fn exception(self, _transport: &mut Transport, 140 | reason: Exception, _scope: &mut Scope) 141 | -> Intent 142 | { 143 | writeln!(&mut stderr(), "Error when fetching data: {}", reason).ok(); 144 | Intent::done() 145 | } 146 | fn fatal(self, reason: Exception, _scope: &mut Scope) 147 | -> Option> 148 | { 149 | writeln!(&mut stderr(), "Error when fetching data: {}", reason).ok(); 150 | None 151 | } 152 | } 153 | 154 | fn main() { 155 | env_logger::init().expect("Can't initialize logging"); 156 | let event_loop = rotor::Loop::new(&rotor::Config::new()).unwrap(); 157 | 158 | let mut loop_inst = event_loop.instantiate(Context); 159 | loop_inst.add_machine_with(|scope| { 160 | Persistent::::connect(scope, 161 | ("www.timeapi.org", 80).to_socket_addrs().unwrap() 162 | .collect::>()[0], 163 | ("www.timeapi.org".to_string(), "/utc/now.json".to_string())) 164 | }).unwrap(); 165 | loop_inst.run().unwrap(); 166 | } 167 | -------------------------------------------------------------------------------- /examples/serve.rs: -------------------------------------------------------------------------------- 1 | extern crate rotor; 2 | extern crate rotor_stream; 3 | 4 | use std::io::{Write, stderr}; 5 | use std::error::Error; 6 | use std::time::Duration; 7 | 8 | use rotor::mio::tcp::{TcpListener, TcpStream}; 9 | use rotor::{Scope}; 10 | use rotor_stream::{Accept, Stream, Protocol, Intent, Transport, Exception}; 11 | 12 | 13 | struct Context; 14 | 15 | enum Http { 16 | ReadHeaders, 17 | SendResponse, 18 | } 19 | 20 | impl Protocol for Http { 21 | type Context = Context; 22 | type Socket = TcpStream; 23 | type Seed = (); 24 | fn create(_seed: (), _sock: &mut TcpStream, scope: &mut Scope) 25 | -> Intent 26 | { 27 | Intent::of(Http::ReadHeaders).expect_delimiter(b"\r\n\r\n", 4096) 28 | .deadline(scope.now() + Duration::new(10, 0)) 29 | } 30 | fn bytes_read(self, transport: &mut Transport, 31 | _end: usize, scope: &mut Scope) 32 | -> Intent 33 | { 34 | println!("Request from {:?}", transport.socket().local_addr()); 35 | transport.output().write_all(concat!( 36 | "HTTP/1.0 200 OK\r\n", 37 | "Server: rotor-stream-example-serve\r\n", 38 | "Connection: close\r\n", 39 | "Content-Length: 14\r\n", 40 | "\r\n", 41 | "Hello World!\r\n", 42 | ).as_bytes()).unwrap(); 43 | Intent::of(Http::SendResponse).expect_flush() 44 | .deadline(scope.now() + Duration::new(10, 0)) 45 | } 46 | fn bytes_flushed(self, _transport: &mut Transport, 47 | _scope: &mut Scope) 48 | -> Intent 49 | { 50 | // TODO(tailhook) or maybe start over? 51 | Intent::done() 52 | } 53 | fn timeout(self, _transport: &mut Transport, 54 | _scope: &mut Scope) 55 | -> Intent 56 | { 57 | writeln!(&mut stderr(), "Timeout happened").ok(); 58 | Intent::done() 59 | } 60 | 61 | fn wakeup(self, _transport: &mut Transport, 62 | _scope: &mut Scope) 63 | -> Intent 64 | { 65 | unreachable!(); 66 | } 67 | fn exception(self, _transport: &mut Transport, 68 | reason: Exception, _scope: &mut Scope) 69 | -> Intent 70 | { 71 | writeln!(&mut stderr(), "Error: {}", reason).ok(); 72 | Intent::done() 73 | } 74 | fn fatal(self, reason: Exception, _scope: &mut Scope) 75 | -> Option> 76 | { 77 | writeln!(&mut stderr(), "Error: {}", reason).ok(); 78 | None 79 | } 80 | } 81 | 82 | fn main() { 83 | let mut event_loop = rotor::Loop::new(&rotor::Config::new()).unwrap(); 84 | let lst = TcpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap(); 85 | let ok = event_loop.add_machine_with(|scope| { 86 | Accept::, _>::new(lst, (), scope) 87 | }).is_ok(); 88 | assert!(ok); 89 | event_loop.run(Context).unwrap(); 90 | } 91 | -------------------------------------------------------------------------------- /src/accept.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | 3 | use rotor::{Machine, Response, EventSet, PollOpt, Evented}; 4 | use rotor::{Scope, GenericScope, Void}; 5 | use rotor::mio::{TryAccept}; 6 | 7 | use {StreamSocket, Accept}; 8 | 9 | 10 | /// Trait which must be implemented for a state machine to accept connection 11 | /// 12 | /// This basically provides alternative constructor for the state machine. 13 | pub trait Accepted: Machine { 14 | type Seed: Clone; 15 | type Socket: StreamSocket; 16 | /// The constructor of the state machine from the accepted connection 17 | fn accepted(sock: Self::Socket, seed: ::Seed, 18 | scope: &mut Scope) 19 | -> Response; 20 | } 21 | 22 | 23 | impl Accept 24 | where A: TryAccept + Evented + Any, 25 | M: Accepted, 26 | { 27 | pub fn new(sock: A, 28 | seed: ::Seed, scope: &mut S) 29 | -> Response 30 | { 31 | match scope.register(&sock, EventSet::readable(), PollOpt::edge()) { 32 | Ok(()) => {} 33 | Err(e) => return Response::error(Box::new(e)), 34 | } 35 | Response::ok(Accept::Server(sock, seed)) 36 | } 37 | } 38 | 39 | impl Machine for Accept 40 | where A: TryAccept + Evented + Any, 41 | M: Accepted, 42 | { 43 | type Context = M::Context; 44 | type Seed = (A::Output, ::Seed); 45 | fn create((sock, seed): Self::Seed, scope: &mut Scope) 46 | -> Response 47 | { 48 | M::accepted(sock, seed, scope).wrap(Accept::Connection) 49 | } 50 | 51 | fn ready(self, events: EventSet, scope: &mut Scope) 52 | -> Response 53 | { 54 | match self { 55 | Accept::Server(a, s) => { 56 | match a.accept() { 57 | Ok(Some(sock)) => { 58 | let seed = (sock, s.clone()); 59 | Response::spawn(Accept::Server(a, s), seed) 60 | } 61 | Ok(None) => { 62 | Response::ok(Accept::Server(a, s)) 63 | } 64 | Err(_) => { 65 | // TODO(tailhook) maybe log the error 66 | Response::ok(Accept::Server(a, s)) 67 | } 68 | } 69 | } 70 | Accept::Connection(m) => { 71 | m.ready(events, scope) 72 | .map(Accept::Connection, |_| unreachable!()) 73 | } 74 | } 75 | } 76 | 77 | fn spawned(self, _scope: &mut Scope) 78 | -> Response 79 | { 80 | match self { 81 | Accept::Server(a, s) => { 82 | match a.accept() { 83 | Ok(Some(sock)) => { 84 | let seed = (sock, s.clone()); 85 | Response::spawn(Accept::Server(a, s), seed) 86 | } 87 | Ok(None) => { 88 | Response::ok(Accept::Server(a, s)) 89 | } 90 | Err(_) => { 91 | // TODO(tailhook) maybe log the error 92 | Response::ok(Accept::Server(a, s)) 93 | } 94 | } 95 | } 96 | Accept::Connection(_) => { 97 | unreachable!(); 98 | } 99 | } 100 | } 101 | 102 | fn timeout(self, scope: &mut Scope) 103 | -> Response 104 | { 105 | match self { 106 | Accept::Server(..) => unreachable!(), 107 | Accept::Connection(m) => { 108 | m.timeout(scope).map(Accept::Connection, |_| unreachable!()) 109 | } 110 | } 111 | } 112 | 113 | fn wakeup(self, scope: &mut Scope) 114 | -> Response 115 | { 116 | match self { 117 | me @ Accept::Server(..) => Response::ok(me), 118 | Accept::Connection(m) => { 119 | m.wakeup(scope).map(Accept::Connection, |_| unreachable!()) 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::error::Error; 3 | 4 | /// Protocol returned None right at the start of the stream processing 5 | #[derive(Debug)] 6 | pub struct ProtocolStop; 7 | 8 | impl fmt::Display for ProtocolStop { 9 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 10 | write!(fmt, "ProtocolStop") 11 | } 12 | } 13 | 14 | impl Error for ProtocolStop { 15 | fn cause(&self) -> Option<&Error> { None } 16 | fn description(&self) -> &'static str { 17 | r#"Protocol returned None (which means "stop") at start"# 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/extensions.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use rotor::{Time, Response, GenericScope}; 4 | 5 | 6 | pub trait ResponseExt { 7 | fn deadline_opt(self, deadline: Option