1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
use std::time::Duration;

use rotor::{Scope, Time};

use recvmode::RecvMode;
use super::{Head, Request, ResponseError, ProtocolError};
use super::{Connection};

pub enum Task<M: Client> {
    Sleep(M, Time),
    Request(M, M::Requester),
    Close,
}

/// A state machine that allows to initiate a client-side HTTP request
///
/// Used for all versions of HTTP.
pub trait Client: Sized {
    type Requester: Requester;
    type Seed: Clone + Sized;

    fn create(seed: Self::Seed,
        scope: &mut Scope<<Self::Requester as Requester>::Context>)
        -> Self;

    /// The handler is invoked when connection is succeed or when previous
    /// request has just finished
    ///
    /// To initiate a request, return `Requester` as part of a return value.
    fn connection_idle(self,
        connection: &Connection,
        scope: &mut Scope<<Self::Requester as Requester>::Context>)
        -> Task<Self>;

    /// Error when establishing connection or connection closed when in idle
    fn connection_error(self,
        reason: &ProtocolError,
        scope: &mut Scope<<Self::Requester as Requester>::Context>);

    /// Standard rotor's wakeup handler
    ///
    /// If `connection.is_idle()` you may initiate a new request
    ///
    /// Note: currently we call this action only when there is no request
    /// beign active (otherwise wakeup goes to request state machine), but
    /// we may change it in future to allow request pipelining
    fn wakeup(self,
        connection: &Connection,
        scope: &mut Scope<<Self::Requester as Requester>::Context>)
        -> Task<Self>;

    /// Standard rotor's timeout handler
    ///
    /// If `connection.is_idle()` you may initiate a new request
    ///
    /// Note: currently we call this action only when there is no request
    /// beign active (otherwise timeout goes to request state machine), but
    /// we may change it in future to allow request pipelining
    fn timeout(self,
        connection: &Connection,
        scope: &mut Scope<<Self::Requester as Requester>::Context>)
        -> Task<Self>;

    /// Returns number of seconds to wait for connection to be established
    ///
    /// This timeout is not obeyed for `Persistent` connections
    fn connect_timeout(&self,
        _scope: &mut Scope<<Self::Requester as Requester>::Context>)
        -> Duration
    {
        Duration::new(15, 0)
    }
    /// Idle timeout
    ///
    /// Timeout for keep-alive connection to be idle
    fn idle_timeout(&self,
        _scope: &mut Scope<<Self::Requester as Requester>::Context>)
        -> Duration
    {
        Duration::new(120, 0)
    }
}

/// A handler of a single client-side HTTP
///
/// Used for all versions of HTTP.
///
/// Note: the interface allows you to receive the response before the whole
/// request is sent to the server. This is only occasionally useful (for
/// example if your server is some kind of encoding service that processes
/// data chunk by chunk). But if response header for some reason is received
/// before request is sent, the connection will be closed, as it probably
/// means that either server is misbehaving or we encounter some
/// out of sync behavior (which is enforced by rust type system so should
/// never happen, unless there is a bug in rotor-http), otherwise we may
/// have a cache poisoning security issue.
///
/// Another property of this state machine is that when any event handler
/// before `request_end()` returns None, the connection is closed (because we
/// don't know whether response is valid and if it's too long to wait it's
/// response). So in case you need to discard the response and it's more cheap
/// than reopening a connection, you must read and ignore it yourself.
pub trait Requester: Sized {
    type Context;

    /// Populates a request
    ///
    /// This receives a `Request` object, which is a "builder". When you
    /// add things to it they are written directly to the buffer. This way
    /// you don't have to allocate temporary memory for map of headers in
    /// case you construct request programmatically.
    ///
    /// While you can continue sending request *body* when response headers
    /// are received. You must either send request *headers* in this handler
    /// or arrange this state machine to be waken up, because no actions will
    /// be invoked later unless response headers are sent.
    fn prepare_request(self, req: &mut Request,
        scope: &mut Scope<Self::Context>) -> Option<Self>;

    /// Encountered when headers received
    ///
    /// Returns self, mode and timeout for reading whole response.
    ///
    /// This handler decides whether response is fully buffered or whether
    /// we need to read response body by chunk. It's recommended to return
    /// Buffered up to certain size, or at least for zero-length response.
    ///
    /// Note that `head` is passed here once, and forgotten by the
    /// protocol. If you need it later it's your responsibility to store it
    /// somewhere.
    fn headers_received(self, head: Head, request: &mut Request,
        scope: &mut Scope<Self::Context>)
        -> Option<(Self, RecvMode, Time)>;

    /// Called when full response is received in buffered mode
    ///
    /// Note: you can't continue with connection here. But you can finish
    /// the request (although, it's probably doesn't make too much sense)
    fn response_received(self, data: &[u8], request: &mut Request,
        scope: &mut Scope<Self::Context>);

    /// Called when response become invalid between `prepare_request()`
    /// and `response_received/response_end`
    ///
    /// This is useful mostly to notify the requestor that it will not have
    /// anything. Note this event doesnt' relate to any HTTP status codes.
    /// They are treated as normal responses by the state machine.
    ///
    /// It's never called on a timeout.
    fn bad_response(self, _error: &ResponseError,
        _scope: &mut Scope<Self::Context>)
    {}

    /// Received chunk of data
    ///
    /// Whey you return `Progressive(nbytes)` from headers received, you
    /// may expect than chunk will be at least of `nbytes` of length. But
    /// you must not rely on that for few reasons:
    ///
    /// 1. Last chunk of response body may be smaller
    /// 2. Chunk is read up to some buffer size, which is heuristically
    ///    determined, and is usually larger than `nbytes`
    /// 3. Currently for chunked encoding we don't merge chunks, so last
    ///    part of each chunk may be shorter as `nbytes`
    fn response_chunk(self, chunk: &[u8], request: &mut Request,
        scope: &mut Scope<Self::Context>)
        -> Option<Self>;

    /// End of response body, only for Progressive responses
    ///
    /// Note: you can't continue with connection here. But you can finish
    /// the request (although, it's probably doesn't make too much sense)
    fn response_end(self, request: &mut Request,
        scope: &mut Scope<Self::Context>);

    /// Request timeout occured
    ///
    /// Unless you've returned the new timeout connection will be closed after
    /// the event.
    fn timeout(self, request: &mut Request, scope: &mut Scope<Self::Context>)
        -> Option<(Self, Time)>;
    fn wakeup(self, request: &mut Request, scope: &mut Scope<Self::Context>)
        -> Option<Self>;

    /// Returns number of seconds between any read/write operation to wait
    /// until connection is closed as stalled
    ///
    /// This doesn't influence idle connections (where no active request is
    /// going on). Use `Client::idle_connection` to set the latter.
    fn byte_timeout(&self, _scope: &mut Scope<Self::Context>) -> Duration {
        Duration::new(120, 0)
    }
}