1 //
2 //   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
3 //   Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 
20 #ifndef GNASH_LIBNET_HTTP_H
21 #define GNASH_LIBNET_HTTP_H
22 
23 #include <string>
24 #include <map>
25 #include <vector>
26 #include <sstream>
27 
28 #ifdef HAVE_CONFIG_H
29 #include "gnashconfig.h"
30 #endif
31 
32 #include "amf.h"
33 #include "cque.h"
34 #include "rtmp.h"
35 //#include "handler.h"
36 #include "network.h"
37 #include "buffer.h"
38 #include "diskstream.h"
39 
40 namespace gnash
41 {
42 
43 class DSOEXPORT HTTP : public gnash::Network
44 {
45 public:
46 // as defined by the W3: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
47     typedef enum {
48         // 1xx: Informational - Request received, continuing process
49         CONTINUE = 100,
50         SWITCHPROTOCOLS = 101,
51         // 2xx: Success - The action was successfully received,
52         // understood, and accepted
53         OK = 200,
54         CREATED = 201,
55         ACCEPTED = 202,
56         NON_AUTHORITATIVE = 203,
57         NO_CONTENT = 204,
58         RESET_CONTENT = 205,
59         PARTIAL_CONTENT = 206,
60         // 3xx: Redirection - Further action must be taken in order to
61         // complete the request
62         MULTIPLE_CHOICES = 300,
63         MOVED_PERMANENTLY = 301,
64         FOUND = 302,
65         SEE_OTHER = 303,
66         NOT_MODIFIED = 304,
67         USE_PROXY = 305,
68         TEMPORARY_REDIRECT = 307,
69         // 4xx: Client Error - The request contains bad syntax or
70         // cannot be fulfilled
71         BAD_REQUEST = 400,
72         UNAUTHORIZED = 401,
73         PAYMENT_REQUIRED = 402,
74         FORBIDDEN = 403,
75         NOT_FOUND = 404,
76         METHOD_NOT_ALLOWED = 405,
77         NOT_ACCEPTABLE = 406,
78         PROXY_AUTHENTICATION_REQUIRED = 407,
79         REQUEST_TIMEOUT = 408,
80         CONFLICT = 409,
81         GONE = 410,
82         LENGTH_REQUIRED = 411,
83         PRECONDITION_FAILED = 412,
84         REQUEST_ENTITY_TOO_LARGE = 413,
85         REQUEST_URI_TOO_LARGE = 414,
86         UNSUPPORTED_MEDIA_TYPE = 415,
87         REQUESTED_RANGE_NOT_SATISFIABLE = 416,
88         EXPECTATION_FAILED = 417,
89         // 5xx: Server Error - The server failed to fulfill an apparently valid request
90         INTERNAL_SERVER_ERROR = 500,
91         NOT_IMPLEMENTED = 501,
92         BAD_GATEWAY = 502,
93         SERVICE_UNAVAILABLE = 503,
94         GATEWAY_TIMEOUT = 504,
95         HTTP_VERSION_NOT_SUPPORTED = 505,
96 	// Gnash/Cygnal extensions for internal use
97 	LIFE_IS_GOOD = 1234,
98 	CLOSEPIPE = 1235
99     } http_status_e;
100     typedef enum {
101 	HTTP_NONE,
102         HTTP_OPTIONS,
103         HTTP_GET,
104         HTTP_HEAD,
105         HTTP_POST,
106         HTTP_PUT,
107         HTTP_DELETE,
108         HTTP_TRACE,
109         HTTP_CONNECT,
110 	HTTP_RESPONSE		// unique to gnash
111     } http_method_e;
112     typedef enum {
113 	OPEN,
114 	SEND,
115 	IDLE,
116 	CLOSE
117     } rtmpt_cmd_e;
118     // A response from an FTTP request has a code an an error message
119     typedef struct {
120 	http_status_e code;
121 	std::string   msg;
122     } http_response_t;
123     typedef struct {
124 	int major;
125 	int minor;
126     } http_version_t;
127     HTTP();
128 //     HTTP(Handler *hand);
129     ~HTTP();
130 
131     // Check the Header fields to make sure they're valid values.
132     bool checkRequestFields(cygnal::Buffer &buf);
133     bool checkEntityFields(cygnal::Buffer &buf);
134     bool checkGeneralFields(cygnal::Buffer &buf);
135 
136 //     // Parse an Echo Request message coming from the Red5 echo_test.
parseEchoRequest(cygnal::Buffer & buf)137     std::vector<std::shared_ptr<cygnal::Element > > parseEchoRequest(cygnal::Buffer &buf) { return parseEchoRequest(buf.reference(), buf.size()); };
138     std::vector<std::shared_ptr<cygnal::Element > > parseEchoRequest(std::uint8_t *buf, size_t size);
139 
140     // Convert the Content-Length field to a number we can use
141     size_t getContentLength();
142 
143     // process all the header fields in the Buffer, storing them internally
144     // in _fields. The address returned is the address where the Content data
145     // starts, and is "Content-Length" bytes long, of "Content-Type" data.
146     std::uint8_t *processHeaderFields(cygnal::Buffer *buf);
147 
148     // Get the field for header 'name' that was stored by processHeaderFields()
getField(const std::string & name)149     std::string &getField(const std::string &name) { return _fields[name]; };
NumOfFields()150     size_t NumOfFields() { return _fields.size(); };
clearFields()151     void clearFields() { _fields.clear(); };
getFields()152     std::map<std::string, std::string> &getFields() { return _fields; };
153 
154     // Get an array of values for header field 'name'.
155     std::shared_ptr<std::vector<std::string> > getFieldItem(const std::string &name);
156 
157     // Client side parsing of response message codes
158     std::shared_ptr<http_response_t> parseStatus(const std::string &line);
159 
160     // Handle the response for the request.
161     std::shared_ptr<cygnal::Buffer> formatServerReply(http_status_e code);
162     cygnal::Buffer &formatGetReply(DiskStream::filetype_e type, size_t size, http_status_e code);
163     cygnal::Buffer &formatGetReply(size_t size, http_status_e code);
164     cygnal::Buffer &formatGetReply(http_status_e code);
165     cygnal::Buffer &formatPostReply(rtmpt_cmd_e code);
166 
167     // Make copies of ourself
168     HTTP &operator = (HTTP &obj);
169 
170     /// @note These methods add data to the fields in the HTTP header.
171     /// \brief clear the data in the stored header
172     bool clearHeader();
173 
174     /// \brief Start constructing a new HTTP header.
175     ///		As it's hard to predict how much storage to allocate,
176     ///		all of these methods for formatting  HTTP header
177     ///		fields store the header while adding data to it. It
178     ///		requires another function to actually send the data.
179     bool startHeader();
180 
181     /// \brief Format the common header fields that need no other processing.
182     ///		Most of these fields are purely ASCII based, and so
183     ///		chare a common constructor. A few require formatting
184     ///		of numerical data into string data, so they can't use
185     ///		the common form.
186     cygnal::Buffer &formatCommon(const std::string &data);
187 
188     cygnal::Buffer &formatHeader(DiskStream::filetype_e type, size_t filesize,
189 			    http_status_e code);
190     cygnal::Buffer &formatHeader(size_t filesize, http_status_e type);
191     cygnal::Buffer &formatHeader(http_status_e type);
192     cygnal::Buffer &formatRequest(const std::string &url, http_method_e req);
193     // format a response to the 'echo' test used for testing Gnash.
194     cygnal::Buffer &formatEchoResponse(const std::string &num, cygnal::Element &el);
195     cygnal::Buffer &formatEchoResponse(const std::string &num, cygnal::Buffer &data);
196     cygnal::Buffer &formatEchoResponse(const std::string &num, std::uint8_t *data, size_t size);
197 
formatMethod(const std::string & data)198     cygnal::Buffer &formatMethod(const std::string &data)
199  	{return formatCommon("Method: " + data); };
200     cygnal::Buffer &formatDate();
201     cygnal::Buffer &formatServer();
202     cygnal::Buffer &formatServer(const std::string &data);
formatReferer(const std::string & data)203     cygnal::Buffer &formatReferer(const std::string &data)
204  	{return formatCommon("Referer: " + data); };
formatConnection(const std::string & data)205     cygnal::Buffer &formatConnection(const std::string &data)
206  	{return formatCommon("Connection: " + data); };
formatKeepAlive(const std::string & data)207     cygnal::Buffer &formatKeepAlive(const std::string &data)
208  	{return formatCommon("Keep-Alive: " + data); };
209     cygnal::Buffer &formatContentLength();
210     cygnal::Buffer &formatContentLength(std::uint32_t filesize);
211     cygnal::Buffer &formatContentType();
212     cygnal::Buffer &formatContentType(DiskStream::filetype_e type);
formatHost(const std::string & data)213     cygnal::Buffer &formatHost(const std::string &data)
214  	{return formatCommon("Host: " + data); };
formatAgent(const std::string & data)215     cygnal::Buffer &formatAgent(const std::string &data)
216  	{return formatCommon("User-Agent: " + data); };
formatAcceptRanges(const std::string & data)217     cygnal::Buffer &formatAcceptRanges(const std::string &data)
218  	{return formatCommon("Accept-Ranges: " + data); };
219     cygnal::Buffer &formatLastModified();
formatLastModified(const std::string & data)220     cygnal::Buffer &formatLastModified(const std::string &data)
221  	{return formatCommon("Last-Modified: " + data); }
formatEtag(const std::string & data)222     cygnal::Buffer &formatEtag(const std::string &data)
223  	{return formatCommon("Etag: " + data); };
formatLanguage(const std::string & data)224     cygnal::Buffer &formatLanguage(const std::string &data)
225  	{return formatCommon("Accept-Language: " + data); };
formatCharset(const std::string & data)226     cygnal::Buffer &formatCharset(const std::string &data)
227  	{return formatCommon("Accept-Charset: " + data); };
formatEncoding(const std::string & data)228     cygnal::Buffer &formatEncoding(const std::string &data)
229  	{return formatCommon("Accept-Encoding: " + data); };
formatTE(const std::string & data)230     cygnal::Buffer &formatTE(const std::string &data)
231  	{return formatCommon("TE: " + data); };
232     // All HTTP messages are terminated with a blank line
terminateHeader()233     void terminateHeader() { _buffer += "\r\n"; };
234 
235 //     cygnal::Buffer &formatErrorResponse(http_status_e err);
236 
237     // Return the header that's been built up.
getHeader()238     std::uint8_t *getHeader() { return _buffer.reference(); };
239 
240     // Return the header that's been built up.
getBuffer()241     cygnal::Buffer &getBuffer() { return _buffer; };
242 
243 //     // Return the body that's been built up.
244 //     std::string getBody() { return _body.str(); };
245 
246     // Get the file type, so we know how to set the
247     // Content-type in the header.
248 //    filetype_e getFileType(std::string &filespec);
249 //    amf::AMF::filetype_e getFileStats(std::string &filespec);
250     void dump();
251 
252     /// \brief Receive a message from the other end of the network connection.
253     ///
254     /// @param fd The file descriptor to read from
255     ///
256     /// @return The number of bytes sent
257     int recvMsg(int fd);
258     int recvMsg(int fd, size_t size);
259 
260     size_t recvChunked(std::uint8_t *data, size_t size);
261 
262     /// \brief Send a message to the other end of the network connection.
263     ///
264     /// @param data A real pointer to the data.
265     /// @param size The number of bytes of data stored.
266     /// @param buf A smart pointer to a Buffer class.
267     /// @param sstr A smart pointer to a Buffer class.
268     /// @param fd The file descriptor to use for writing to the network.
269     /// @param void Send the contents of the _header and _body.
270     ///
271     /// @return The number of bytes sent
272     int sendMsg();
273     int sendMsg(int fd);
274     int sendMsg(const std::uint8_t *data, size_t size);
sendMsg(std::shared_ptr<cygnal::Buffer> & buf)275     int sendMsg(std::shared_ptr<cygnal::Buffer> &buf)
276 	{ return sendMsg(buf->reference(), buf->size()); };
sendMsg(std::stringstream & sstr)277     int sendMsg(std::stringstream &sstr)
278 	{ return sendMsg(reinterpret_cast<const std::uint8_t *>(sstr.str().c_str()), sstr.str().size()); };
279 
280     // These accessors are used mostly just for debugging.
keepAlive()281     bool keepAlive() { return _keepalive; }
keepAlive(bool x)282     void keepAlive(bool x) { _keepalive = x; };
283 
getMaxRequests()284     int getMaxRequests() { return _max_requests; }
getFileSize()285     int getFileSize() { return _filesize; }
getFilespec()286     std::string &getFilespec() { return _filespec; }
getParams()287     std::string &getParams() { return _params; }
288   //    std::string &getURL() { return _url; }
getStatusCodes()289     std::map<int, struct status_codes *> getStatusCodes()
290 	{ return _status_codes; }
getVersion()291     http_version_t *getVersion() { return &_version; }
292 
293 //     void setHandler(Handler *hand) { _handler = hand; };
setDocRoot(const std::string & path)294     void setDocRoot(const std::string &path) { _docroot = path; };
getDocRoot()295     std::string &getDocRoot() { return _docroot; };
296 
297     // Pop the first date element off the que
popChunk()298     std::shared_ptr<cygnal::Buffer> DSOEXPORT popChunk() { return _que.pop(); };
299     // Peek at the first date element witjhout removing it from the que
peekChunk()300     std::shared_ptr<cygnal::Buffer> DSOEXPORT peekChunk() { return _que.peek(); };
301     // Get the number of elements in the que
sizeChunks()302     size_t DSOEXPORT sizeChunks() { return _que.size(); };
303 
mergeChunks()304     std::shared_ptr<cygnal::Buffer> DSOEXPORT mergeChunks() { return _que.merge(); };
305 
getOperation()306     http_method_e getOperation() { return _cmd; };
307 
308 protected:
309     // Examine the beginning of the data for an HTTP request command
310     // like GET or POST, etc...
311     http_method_e extractCommand(std::uint8_t *data);
extractCommand(cygnal::Buffer & data)312     http_method_e extractCommand(cygnal::Buffer &data)
313 	{ return extractCommand(data.reference()); };
314 
315     typedef boost::char_separator<char> Sep;
316     typedef boost::tokenizer<Sep> Tok;
317     http_method_e	_cmd;
318 
319     cygnal::Buffer		_buffer;
320     CQue		_que;
321 
322     DiskStream::filetype_e  _filetype;
323     std::string		_filespec;
324     std::string		_params;
325     std::uint32_t     _filesize;
326     std::map<int, struct status_codes *> _status_codes;
327 
328     std::map<std::string, std::string> _fields;
329     http_version_t	_version;
330 
331     // Connection parameters we care about
332     bool		_keepalive;
333 //     Handler		*_handler;
334     // These two field hold the data from an RTMPT message
335     int			_clientid;
336     int			_index;
337     int			_max_requests;
338     std::string		_docroot;
339 
340     bool		_close;
341 };
342 
343 // This is the thread for all incoming HTTP connections for the server
344 extern "C" {
345     bool DSOEXPORT http_handler(Network::thread_params_t *args);
346 }
347 
348 
349 } // end of gnash namespace
350 
351 // end of _HTTP_H_
352 #endif
353 
354 
355 // local Variables:
356 // mode: C++
357 // indent-tabs-mode: t
358 // End:
359