PageRenderTime 33ms CodeModel.GetById 23ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/http/server.h

http://github.com/mozy/mordor
C Header | 223 lines | 116 code | 36 blank | 71 comment | 0 complexity | 152e554ba25bdf243090f06a2bd3d891 MD5 | raw file
  1#ifndef __MORDOR_HTTP_SERVER_H__
  2#define __MORDOR_HTTP_SERVER_H__
  3// Copyright (c) 2009 - Mozy, Inc.
  4
  5#include <boost/thread/recursive_mutex.hpp>
  6
  7#include "connection.h"
  8
  9namespace Mordor {
 10
 11class Fiber;
 12class Multipart;
 13class Scheduler;
 14
 15namespace HTTP {
 16
 17class ServerConnection;
 18
 19class ServerRequest : public boost::enable_shared_from_this<ServerRequest>, boost::noncopyable
 20{
 21private:
 22    friend class ServerConnection;
 23public:
 24    typedef boost::shared_ptr<ServerRequest> ptr;
 25    typedef boost::shared_ptr<const ServerRequest> const_ptr;
 26
 27    enum State {
 28        PENDING,
 29        WAITING,
 30        HEADERS,
 31        BODY,
 32        COMPLETE,
 33        ERROR
 34    };
 35
 36private:
 37    ServerRequest(boost::shared_ptr<ServerConnection> conn);
 38
 39public:
 40    ~ServerRequest();
 41
 42    const Request &request() const { return m_request; }
 43    bool hasRequestBody() const;
 44    /// A stream representing the request body
 45    ///
 46    /// The stream will be fully "decoded" according to the request headers.
 47    /// I.e. transfer encodings will already be applied, and it will have a
 48    /// size if Content-Length is set.
 49    /// @pre hasRequestBody()
 50    boost::shared_ptr<Stream> requestStream();
 51    const EntityHeaders &requestTrailer() const;
 52    boost::shared_ptr<Multipart> requestMultipart();
 53
 54    /// Response Headers
 55    ///
 56    /// Changes to the headers will not do anything if the response has already
 57    /// been committed().
 58    Response &response() { return m_response; }
 59    const Response &response() const { return m_response; }
 60    bool hasResponseBody() const;
 61
 62    /// A stream representing the response body
 63    ///
 64    /// Only the actual response needs to be written.  Transfer encodings
 65    /// will be automatically applied, and you will be unable to write beyond
 66    /// the size of Content-Length was set.  The response headers will be
 67    /// automatically committed if they have not been committed already
 68    boost::shared_ptr<Stream> responseStream();
 69    boost::shared_ptr<Multipart> responseMultipart();
 70    EntityHeaders &responseTrailer();
 71
 72    boost::shared_ptr<ServerConnection> connection() { return m_conn; }
 73
 74    bool committed() const { return m_responseState >= HEADERS; }
 75
 76    /// Start reading the next request
 77    ///
 78    /// Even if this request hasn't responded yet; enables pipelining.
 79    /// If this request isn't complete, it will set a flag to immediately
 80    /// start reading the next request as soon as this one is complete.
 81    void processNextRequest();
 82
 83    /// Abort the request
 84    ///
 85    /// Aborts this request, and any subsequent requests or responses
 86    void cancel();
 87    void finish();
 88
 89    /// Context
 90    ///
 91    /// Context of the ServerRequest
 92    const std::string & context() const { return m_context; }
 93    unsigned long long requestNumber() const { return m_requestNumber; }
 94    /// ignore request body
 95    void discardRequestBody();
 96
 97    unsigned long long startTime() const { return m_startTime; }
 98
 99private:
100    void doRequest();
101    void commit();
102    void finishRequest();
103    void requestDone();
104    void responseMultipartDone();
105    void responseDone();
106    void readFinish();
107
108private:
109    boost::shared_ptr<ServerConnection> m_conn;
110    unsigned long long m_requestNumber;
111    Scheduler *m_scheduler;
112    boost::shared_ptr<Fiber> m_fiber;
113    Request m_request;
114    Response m_response;
115    EntityHeaders m_requestTrailer, m_responseTrailer;
116    State m_requestState, m_responseState;
117    bool m_willClose, m_pipeline;
118    boost::shared_ptr<Stream> m_requestStream, m_responseStream;
119    boost::shared_ptr<Multipart> m_requestMultipart, m_responseMultipart;
120    std::string m_context;
121    unsigned long long m_startTime;
122};
123
124/// Individual connection to an HTTP server
125///
126/// A ServerConnection operates over any reliable, full-duplex stream. An HTTP
127/// server needs to establish the physical connection, create a
128/// ServerConnection, and call processRequests.  The ServerConnection will then
129/// manager the underlying stream, and read requests off of it.  As each
130/// request is read, dg is called with a ServerRequest object, and is
131/// responsible for responding to it.  If dg returns without fulfilling any of
132/// its duties (reading the request body, responding, exception), it will be
133/// dealt with appropriately:
134///  * Responding 500 if an exception happened before the headers were
135///    committed
136///  * Closing the connection if an exception happened after the headers were
137///    committed
138///  * Closing the connection if the response body was not fully written
139///  * Reading the rest of the request body if it was not fully read
140/// The connection will automatically manage keep alive, and responding to
141/// unparseable and invalid requests (i.e. no Host header for HTTP/1.1).
142/// It fully supports server side pipelining, but it is an opt-in feature:
143/// ServerRequest::processNextRequest() must be called before the server will
144/// start reading a pipelined request.
145class ServerConnection : public Connection,
146    public boost::enable_shared_from_this<ServerConnection>, boost::noncopyable
147{
148public:
149    typedef boost::shared_ptr<ServerConnection> ptr;
150    typedef boost::weak_ptr<ServerConnection> weak_ptr;
151
152private:
153    friend class ServerRequest;
154public:
155    /// @param id   the unique id that helps to identify the ServerConnection,
156    ///           useful for trouble shoot
157    ServerConnection(boost::shared_ptr<Stream> stream,
158        boost::function<void (ServerRequest::ptr)> dg,
159        const std::string &idi = std::string());
160
161    /// Does not block; simply schedules a new fiber to read the first request
162    void processRequests();
163
164    std::vector<ServerRequest::const_ptr> requests();
165
166    void cancel();
167
168private:
169    void scheduleNextRequest(ServerRequest *currentRequest);
170    void requestComplete(ServerRequest *currentRequest);
171    void responseComplete(ServerRequest *currentRequest);
172    void scheduleAllWaitingResponses();
173    void onClientConnectionClose(weak_ptr self);
174
175private:
176    boost::function<void (ServerRequest::ptr)> m_dg;
177    boost::recursive_mutex m_mutex;
178    std::list<ServerRequest *> m_pendingRequests;
179    std::set<ServerRequest *> m_waitingResponses;
180    unsigned long long m_requestCount, m_priorRequestFailed,
181        m_priorRequestClosed, m_priorResponseClosed;
182
183    void invariant() const;
184
185private:
186    const std::string & context() const { return m_context; }
187
188private:
189    std::string m_context;
190    bool m_clientClosed;
191};
192
193// Helper functions
194/// Respond with a status code
195///
196/// This will clear Transfer-Encoding, Content-Length, Content-Type,
197/// and optionally ETag headers.
198/// @param message The message to be used as the body of the response
199///                (Content-Type will be set to text/plain)
200void respondError(ServerRequest::ptr request, Status status,
201    const std::string &message = std::string(), bool closeConnection = false,
202    bool clearETag = true);
203
204/// Respond with a Stream
205///
206/// This function will process Range, If-Range, and TE headers to stream
207/// response in the most efficient way, applying transfer encodings if
208/// necessary or possible
209void respondStream(ServerRequest::ptr request, Stream &response);
210void respondStream(ServerRequest::ptr request,
211    boost::shared_ptr<Stream> response);
212
213/// Procesess If-Match, If-None-Match headers
214///
215/// @param request The request
216/// @param eTag The ETag of the entity currently
217/// @return If the request should continue; otherwise the error has already
218///         been processed, and the request is complete
219bool ifMatch(ServerRequest::ptr request, const ETag &eTag);
220
221}}
222
223#endif