1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4  *
5  * All rights reserved.
6  */
7 //
8 // reply.hpp
9 // ~~~~~~~~~
10 //
11 // Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
12 //
13 // Distributed under the Boost Software License, Version 1.0. (See accompanying
14 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
15 //
16 
17 #ifndef HTTP_REPLY_HPP
18 #define HTTP_REPLY_HPP
19 
20 #include <time.h>
21 
22 #include <list>
23 #include <string>
24 #include <vector>
25 
26 #include <Wt/AsioWrapper/asio.hpp>
27 
28 #ifdef WTHTTP_WITH_ZLIB
29 #include <zlib.h>
30 #endif
31 
32 #include "Wt/WStringStream.h"
33 #include "Wt/WLogger.h"
34 #include "../web/Configuration.h"
35 
36 #include "Buffer.h"
37 #include "WHttpDllDefs.h"
38 #include "Request.h"
39 
40 namespace http {
41 namespace server {
42 
43 namespace asio = Wt::AsioWrapper::asio;
44 
45 class Configuration;
46 class Connection;
47 class Reply;
48 
49 typedef std::shared_ptr<Connection> ConnectionPtr;
50 typedef std::shared_ptr<Reply> ReplyPtr;
51 
52 class WTHTTP_API Reply : public std::enable_shared_from_this<Reply>
53 {
54 public:
55   Reply(Request& request, const Configuration& config);
56   virtual ~Reply();
57 
58   virtual void reset(const Wt::EntryPoint *ep);
59 
60   enum status_type
61   {
62     no_status = 0,
63     switching_protocols = 101,
64     ok = 200,
65     created = 201,
66     accepted = 202,
67     no_content = 204,
68     partial_content = 206,
69     multiple_choices = 300,
70     moved_permanently = 301,
71     found = 302,
72     see_other = 303,
73     not_modified = 304,
74     moved_temporarily = 307,
75     bad_request = 400,
76     unauthorized = 401,
77     forbidden = 403,
78     not_found = 404,
79     request_entity_too_large = 413,
80     requested_range_not_satisfiable = 416,
81     internal_server_error = 500,
82     not_implemented = 501,
83     bad_gateway = 502,
84     service_unavailable = 503,
85     version_not_supported = 505
86   };
87 
88   enum ws_opcode {
89     continuation = 0x0,
90     text_frame = 0x1,
91     binary_frame = 0x2,
92     connection_close = 0x8,
93     ping = 0x9,
94     pong = 0xA,
95   };
96 
97   /*
98    * Called after writing data.
99    *
100    * In case the write was successful, this may call send() again if
101    * more data is immediately available.
102    */
103   virtual void writeDone(bool success);
104 
105   /*
106    * Returns true if ready to read more.
107    */
108   virtual bool consumeData(const char *begin,
109 			   const char *end,
110 			   Request::State state) = 0;
111 
112   /*
113    * Returns false on failure (should abort reading more)
114    */
115   virtual bool consumeWebSocketMessage(ws_opcode opcode,
116 				       const char* begin,
117 				       const char* end,
118 				       Request::State state);
119 
120   void setConnection(ConnectionPtr connection);
121   bool nextWrappedContentBuffers(std::vector<asio::const_buffer>& result);
122   bool nextBuffers(std::vector<asio::const_buffer>& result);
123   bool closeConnection() const;
setCloseConnection()124   void setCloseConnection() { closeConnection_ = true; }
125   void detectDisconnect(const std::function<void()>& callback);
126 
127 
128   void addHeader(const std::string name, const std::string value);
129 
130   void receive();
131   void send();
132 
configuration()133   const Configuration& configuration() { return configuration_; }
134 
135   virtual void logReply(Wt::WLogger& logger);
136   void setStatus(status_type status);
status()137   status_type status() const { return status_; }
138 
139 protected:
140   Request& request_;
141   const Configuration& configuration_;
142 
143   virtual std::string contentType() = 0;
144   virtual std::string location();
145   virtual ::int64_t contentLength() = 0;
146 
147   /*
148    * Provides next data to send. Will be called after send() has been called.
149    * Returns whether this is the last data for this request.
150    */
151   virtual bool nextContentBuffers(std::vector<asio::const_buffer>& result)
152     = 0;
153 
154   void setRelay(ReplyPtr reply);
relay()155   ReplyPtr relay() const { return relay_; }
156 
157   static std::string httpDate(time_t t);
158 
connection()159   ConnectionPtr connection() const { return connection_; }
transmitting()160   bool transmitting() const { return transmitting_; }
161   asio::const_buffer buf(const std::string &s);
162 
163 private:
164 
165   std::vector<std::pair<std::string, std::string> > headers_;
166 
167   ConnectionPtr connection_;
168 
169   status_type status_;
170   bool transmitting_;
171   bool closeConnection_;
172   bool chunkedEncoding_;
173   bool gzipEncoding_;
174 
175   ::int64_t contentSent_;
176   ::int64_t contentOriginalSize_;
177 
178   ReplyPtr relay_;
179 
180   Wt::WStringStream buf_;
181   Wt::WStringStream postBuf_;
182   // don't use vector; on resize the strings in bufs_ are copied, causing the
183   // pointers in the asio buffer lists to become invalid
184   std::list<std::string> bufs_;
185 
186 
187   bool encodeNextContentBuffer(std::vector<asio::const_buffer>& result,
188 			       int& originalSize, int& encodedSize);
189 #ifdef WTHTTP_WITH_ZLIB
190   void initGzip();
191   bool gzipBusy_;
192   z_stream gzipStrm_;
193 #endif
194 };
195 
196 typedef std::shared_ptr<Reply> ReplyPtr;
197 
198 } // namespace server
199 } // namespace http
200 
201 #endif // HTTP_REPLY_HPP
202