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