/mordor/http/server.h
C Header | 223 lines | 116 code | 36 blank | 71 comment | 0 complexity | 152e554ba25bdf243090f06a2bd3d891 MD5 | raw file
Possible License(s): BSD-3-Clause
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