1
// http.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
3
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
6
// This program is free software; you can redistribute it and/or modify
7
// it under the terms of the GNU General Public License as published by
8
// the Free Software Foundation; either version 3 of the License, or
9
// (at your option) any later version.
11
// This program is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
// GNU General Public License for more details.
16
// You should have received a copy of the GNU General Public License
17
// along with this program; if not, write to the Free Software
18
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
#include "gnashconfig.h"
25
#include <boost/thread/mutex.hpp>
26
#include <boost/shared_ptr.hpp>
27
#include <boost/shared_array.hpp>
28
#include <boost/scoped_array.hpp>
29
#include <boost/tokenizer.hpp>
30
#include <boost/date_time/posix_time/posix_time.hpp>
31
#include <boost/date_time/gregorian/gregorian.hpp>
32
#include <sys/types.h>
39
#include <sys/types.h>
42
#include "GnashSystemIOHeaders.h" // read()
50
//#include "handler.h"
54
#include "diskstream.h"
56
// Cygnal specific headers
57
#include "http_server.h"
61
// Not POSIX, so best not rely on it if possible.
63
# define PATH_MAX 1024
66
using namespace gnash;
69
static boost::mutex stl_mutex;
74
// The rcfile is loaded and parsed here:
75
static CRcInitFile& crcfile = CRcInitFile::getDefaultInstance();
76
static Cache& cache = Cache::getDefaultInstance();
77
// static Proc& cgis = Proc::getDefaultInstance();
79
HTTPServer::HTTPServer()
81
// GNASH_REPORT_FUNCTION;
84
HTTPServer::~HTTPServer()
86
// GNASH_REPORT_FUNCTION;
90
HTTPServer::processClientRequest(int fd)
92
GNASH_REPORT_FUNCTION;
94
cygnal::Buffer *buf = new cygnal::Buffer;
96
// return processClientRequest(fd, buf);
97
return HTTP::HTTP_NONE;
101
HTTPServer::processClientRequest(Handler *hand, int fd, cygnal::Buffer *buf)
103
GNASH_REPORT_FUNCTION;
105
cygnal::Buffer result;
108
_cmd = extractCommand(buf->reference());
111
result = processGetRequest(hand, fd, buf);
113
case HTTP::HTTP_POST:
114
result = processPostRequest(fd, buf);
116
case HTTP::HTTP_HEAD:
117
result = processHeadRequest(fd, buf);
119
case HTTP::HTTP_CONNECT:
120
result = processConnectRequest(fd, buf);
122
case HTTP::HTTP_TRACE:
123
result = processTraceRequest(fd, buf);
125
case HTTP::HTTP_OPTIONS:
126
result = processOptionsRequest(fd, buf);
129
result = processPutRequest(fd, buf);
131
case HTTP::HTTP_DELETE:
132
result = processDeleteRequest(fd, buf);
141
writeNet(fd, result);
143
_docroot = crcfile.getDocumentRoot();
145
string url = _docroot + _filespec;
147
// See if the file is in the cache and already opened.
148
boost::shared_ptr<DiskStream> filestream(cache.findFile(_filespec));
150
log_debug("FIXME: found filestream %s in cache!", _filespec);
153
filestream.reset(new DiskStream);
154
log_network("New filestream %s", _filespec);
155
// cache.addFile(url, filestream); FIXME: always reload from disk for now.
157
// Oopen the file and read the first chunk into memory
158
if (filestream->open(url)) {
159
formatErrorResponse(HTTPServer::NOT_FOUND);
161
// Get the file size for the HTTPServer header
162
if (filestream->getFileType() == DiskStream::FILETYPE_NONE) {
163
formatErrorResponse(HTTPServer::NOT_FOUND);
165
// cache.addPath(_filespec, filestream->getFilespec());
166
// cache.addFile(_filespec, filestream);
169
// Close the file but leave resident for now.
170
if (filestream->fullyPopulated()) {
173
// cache.addFile(_filespec, filestream);
180
// A GET request asks the server to send a file to the client
182
HTTPServer::processGetRequest(Handler *hand, int fd, cygnal::Buffer *buf)
184
GNASH_REPORT_FUNCTION;
186
// cerr << "QUE = " << _que.size() << endl;
188
// cerr << "YYYYYYY: " << (char *)buf->reference() << endl;
189
// cerr << hexify(buf->reference(), buf->allocated(), false) << endl;
192
// log_debug("Que empty, net connection dropped for fd #%d", getFileFd());
193
log_debug("Que empty, net connection dropped for fd #%d", fd);
199
processHeaderFields(buf);
201
_docroot = crcfile.getDocumentRoot();
203
string url = _docroot + _filespec;
205
boost::shared_ptr<DiskStream> ds = hand->getDiskStream(fd);
210
_diskstream.reset(new DiskStream);
211
log_network("New filestream %s", _filespec);
213
log_network("Reusing filestream %s", _filespec);
216
// Oopen the file and read the first chunk into memory
217
if (_diskstream->open(url)) {
218
formatErrorResponse(HTTPServer::NOT_FOUND);
220
// Get the file size for the HTTPServer header
221
if (_diskstream->getFileType() == DiskStream::FILETYPE_NONE) {
222
formatErrorResponse(HTTPServer::NOT_FOUND);
224
// cache.addPath(_filespec, filestream->getFilespec());
225
// cache.addFile(_filespec, filestream);
228
// Closing the file closes the disk file, but leaves data resident
229
// in memory for future access to this file. If we've been opened,
230
// the next operation is to start writing the file next time
231
// ::play() is called.
232
if (_diskstream->fullyPopulated()) {
233
_diskstream->close();
235
_diskstream->setState(DiskStream::PLAY);
236
// cache.addFile(_filespec, _diskstream);
238
// Create the reply message
239
// _close = true; Force sending the close connection in the header
240
cygnal::Buffer &reply = formatHeader(_diskstream->getFileType(),
241
_diskstream->getFileSize(),
246
size_t filesize = _diskstream->getFileSize();
247
// size_t bytes_read = 0;
251
#ifdef USE_STATS_CACHE
252
struct timespec start;
253
clock_gettime (CLOCK_REALTIME, &start);
256
#ifdef USE_STATS_CACHE
258
clock_gettime (CLOCK_REALTIME, &end);
259
double time = (end.tv_sec - start.tv_sec) + ((end.tv_nsec - start.tv_nsec)/1e9);
260
cerr << "File " << _filespec
261
<< " transferred " << filesize << " bytes in: " << fixed
262
<< time << " seconds for net fd #" << fd << endl;
269
// A POST request asks sends a data from the client to the server. After processing
270
// the header like we normally do, we then read the amount of bytes specified by
271
// the "content-length" field, and then write that data to disk, or decode the amf.
272
boost::shared_ptr<cygnal::Buffer>
273
HTTPServer::processPostRequest(int fd, cygnal::Buffer *bufFIXME)
275
GNASH_REPORT_FUNCTION;
277
// cerr << "QUE1 = " << _que.size() << endl;
279
boost::shared_ptr<cygnal::Buffer> buf;
281
if (_que.size() == 0) {
287
log_debug("Que empty, net connection dropped for fd #%d", getFileFd());
290
// cerr << __FUNCTION__ << buf->allocated() << " : " << hexify(buf->reference(), buf->allocated(), true) << endl;
293
boost::uint8_t *data = processHeaderFields(buf.get());
294
size_t length = strtol(getField("content-length").c_str(), NULL, 0);
295
boost::shared_ptr<cygnal::Buffer> content(new cygnal::Buffer(length));
297
if (buf->allocated() - (data - buf->reference()) ) {
298
// cerr << "Don't need to read more data: have " << buf->allocated() << " bytes" << endl;
299
content->copy(data, length);
302
// cerr << "Need to read more data, only have " << buf->allocated() << " bytes" << endl;
303
ret = readNet(fd, *content, 2);
304
data = content->reference();
307
if (getField("content-type") == "application/x-www-form-urlencoded") {
308
log_debug("Got file data in POST");
309
string url = _docroot + _filespec;
310
DiskStream ds(url, *content);
313
// oh boy, we got ourselves some encoded AMF objects instead of a boring file.
314
} else if (getField("content-type") == "application/x-amf") {
315
log_debug("Got AMF data in POST");
318
boost::shared_ptr<cygnal::Element> el = amf.extractAMF(content.reference(), content.end());
319
el->dump(); // FIXME: do something intelligent
326
// NOTE: this is a "special" path we trap until we have real CGI support
327
if ((getField("content-type") == "application/x-amf")
328
&& (getField("content-type") == "application/x-amf")) {
330
if (_filespec == "/echo/gateway") {
334
string path = _docroot;
336
cgis.startCGI(_filespec, true, CGIBIN_PORT);
337
cgis.createClient("localhost", CGIBIN_PORT);
338
cgis.writeNet(*content);
339
boost::shared_ptr<cygnal::Buffer> reply = cgis.readNet();
341
writeNet(fd, *reply);
342
// cgis.stopCGI(_filespec);
344
vector<boost::shared_ptr<cygnal::Element> > headers = parseEchoRequest(*content);
345
//boost::shared_ptr<cygnal::Element> &el0 = headers[0];
347
if (headers.size() >= 4) {
349
cygnal::Buffer &reply = formatEchoResponse(headers[1]->getName(), *headers[3]);
350
// cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), true) << endl;
351
// cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), false) << endl;
357
cygnal::Buffer &reply = formatHeader(_filetype, _filesize, HTTPServer::OK);
364
boost::shared_ptr<cygnal::Buffer>
365
HTTPServer::processPutRequest(int /* fd */, cygnal::Buffer */* buf */)
367
boost::shared_ptr<cygnal::Buffer> buf;
368
// GNASH_REPORT_FUNCTION;
369
log_unimpl("PUT request");
374
boost::shared_ptr<cygnal::Buffer>
375
HTTPServer::processDeleteRequest(int /* fd */, cygnal::Buffer */* buf */)
377
// GNASH_REPORT_FUNCTION;
378
boost::shared_ptr<cygnal::Buffer> buf;
379
log_unimpl("DELETE request");
384
boost::shared_ptr<cygnal::Buffer>
385
HTTPServer::processConnectRequest(int /* fd */, cygnal::Buffer */* buf */)
387
// GNASH_REPORT_FUNCTION;
388
boost::shared_ptr<cygnal::Buffer> buf;
389
log_unimpl("CONNECT request");
394
boost::shared_ptr<cygnal::Buffer>
395
HTTPServer::processOptionsRequest(int /* fd */, cygnal::Buffer */* buf */)
397
// GNASH_REPORT_FUNCTION;
398
boost::shared_ptr<cygnal::Buffer> buf;
399
log_unimpl("OPTIONS request");
404
boost::shared_ptr<cygnal::Buffer>
405
HTTPServer::processHeadRequest(int /* fd */, cygnal::Buffer */* buf */)
407
// GNASH_REPORT_FUNCTION;
408
boost::shared_ptr<cygnal::Buffer> buf;
409
log_unimpl("HEAD request");
414
boost::shared_ptr<cygnal::Buffer>
415
HTTPServer::processTraceRequest(int /* fd */, cygnal::Buffer */* buf */)
417
// GNASH_REPORT_FUNCTION;
418
boost::shared_ptr<cygnal::Buffer> buf;
419
log_unimpl("TRACE request");
425
HTTPServer::formatErrorResponse(http_status_e code)
427
// GNASH_REPORT_FUNCTION;
430
// First build the message body, so we know how to set Content-Length
431
_buffer += "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n";
432
_buffer += "<html><head>\r\n";
433
_buffer += "<title>";
434
sprintf(num, "%d", code);
436
_buffer += " Not Found</title>\r\n";
437
_buffer += "</head><body>\r\n";
438
_buffer += "<h1>Not Found</h1>\r\n";
439
_buffer += "<p>The requested URL ";
440
_buffer += _filespec;
441
_buffer += " was not found on this server.</p>\r\n";
442
_buffer += "<hr>\r\n";
443
_buffer += "<address>Cygnal (GNU/Linux) Server at ";
444
_buffer += getField("host");
445
_buffer += " </address>\r\n";
446
_buffer += "</body></html>\r\n";
448
// First build the header
451
formatContentLength(_filesize);
452
formatConnection("close");
453
formatContentType(_filetype);
455
// All HTTPServer messages are followed by a blank line.
462
HTTPServer::formatGetReply(http_status_e code)
465
// GNASH_REPORT_FUNCTION;
467
return formatHeader(_filesize, code);
471
HTTPServer::formatGetReply(size_t size, http_status_e code)
473
// GNASH_REPORT_FUNCTION;
475
formatHeader(size, code);
477
// int ret = Network::writeNet(_header.str());
478
// boost::uint8_t *ptr = (boost::uint8_t *)_body.str().c_str();
479
// buf->copy(ptr, _body.str().size());
483
if (_header.str().size()) {
484
log_debug (_("Sent GET Reply"));
488
log_debug (_("Couldn't send GET Reply, no header data"));
496
HTTPServer::formatPostReply(rtmpt_cmd_e /* code */)
498
GNASH_REPORT_FUNCTION;
502
formatContentType(DiskStream::FILETYPE_AMF);
503
// All HTTPServer messages are followed by a blank line.
508
formatHeader(_filesize, code);
509
boost::shared_ptr<cygnal::Buffer> buf = new cygnal::Buffer;
510
if (_header.str().size()) {
511
buf->resize(_header.str().size());
512
string str = _header.str();
514
_handler->pushout(buf);
515
_handler->notifyout();
516
log_debug (_("Sent GET Reply"));
517
return true; // Default to true
520
log_debug (_("Couldn't send POST Reply, no header data"));
528
// Parse an Echo Request message coming from the Red5 echo_test. This
529
// method should only be used for testing purposes.
530
vector<boost::shared_ptr<cygnal::Element > >
531
HTTPServer::parseEchoRequest(boost::uint8_t *data, size_t size)
533
// GNASH_REPORT_FUNCTION;
535
vector<boost::shared_ptr<cygnal::Element > > headers;
537
// skip past the header bytes, we don't care about them.
538
boost::uint8_t *tmpptr = data + 6;
540
boost::uint16_t length;
541
length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
542
tmpptr += sizeof(boost::uint16_t);
544
// Get the first name, which is a raw string, and not preceded by
546
boost::shared_ptr<cygnal::Element > el1(new cygnal::Element);
548
// If the length of the name field is corrupted, then we get out of
549
// range quick, and corrupt memory. This is a bit of a hack, but
550
// reduces memory errors caused by some of the corrupted tes cases.
551
boost::uint8_t *endstr = std::find(tmpptr, tmpptr+length, '\0');
552
if (endstr != tmpptr+length) {
553
log_debug("Caught corrupted string! length was %d, null at %d",
554
length, endstr-tmpptr);
555
length = endstr-tmpptr;
557
el1->setName(tmpptr, length);
559
headers.push_back(el1);
561
// Get the second name, which is a raw string, and not preceded by
563
length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
564
tmpptr += sizeof(boost::uint16_t);
565
boost::shared_ptr<cygnal::Element > el2(new cygnal::Element);
567
// std::string name2(reinterpret_cast<const char *>(tmpptr), length);
568
// el2->setName(name2.c_str(), name2.size());
569
// If the length of the name field is corrupted, then we get out of
570
// range quick, and corrupt memory. This is a bit of a hack, but
571
// reduces memory errors caused by some of the corrupted tes cases.
572
endstr = std::find(tmpptr, tmpptr+length, '\0');
573
if (endstr != tmpptr+length) {
574
log_debug("Caught corrupted string! length was %d, null at %d",
575
length, endstr-tmpptr);
576
length = endstr-tmpptr;
578
el2->setName(tmpptr, length);
579
headers.push_back(el2);
582
// Get the last two pieces of data, which are both AMF encoded
585
boost::shared_ptr<cygnal::Element> el3 = amf.extractAMF(tmpptr, tmpptr + size);
586
headers.push_back(el3);
587
tmpptr += amf.totalsize();
589
boost::shared_ptr<cygnal::Element> el4 = amf.extractAMF(tmpptr, tmpptr + size);
590
headers.push_back(el4);
595
// format a response to the 'echo' test used for testing Gnash. This
596
// is only used for testing by developers. The format appears to be
597
// two strings, followed by a double, followed by the "onResult".
599
HTTPServer::formatEchoResponse(const std::string &num, cygnal::Element &el)
601
// GNASH_REPORT_FUNCTION;
602
boost::shared_ptr<cygnal::Buffer> data;
605
if (el.getType() == cygnal::Element::TYPED_OBJECT_AMF0) {
606
nel.makeTypedObject();
607
string name = el.getName();
609
if (el.propertySize()) {
610
// FIXME: see about using std::reverse() instead.
611
for (int i=el.propertySize()-1; i>=0; i--) {
612
// for (int i=0 ; i<el.propertySize(); i++) {
613
boost::shared_ptr<cygnal::Element> child = el.getProperty(i);
614
nel.addProperty(child);
624
return formatEchoResponse(num, data->reference(), data->allocated());
628
HTTPServer::formatEchoResponse(const std::string &num, cygnal::Buffer &data)
630
// GNASH_REPORT_FUNCTION;
631
return formatEchoResponse(num, data.reference(), data.allocated());
635
HTTPServer::formatEchoResponse(const std::string &num, boost::uint8_t *data, size_t size)
637
// GNASH_REPORT_FUNCTION;
639
//boost::uint8_t *tmpptr = data;
641
// FIXME: temporary hacks while debugging
642
cygnal::Buffer fixme("00 00 00 00 00 01");
643
cygnal::Buffer fixme2("ff ff ff ff");
645
_buffer = "HTTPServer/1.1 200 OK\r\n";
646
formatContentType(DiskStream::FILETYPE_AMF);
647
// formatContentLength(size);
648
// FIXME: this is a hack ! Calculate a real size!
649
formatContentLength(size+29);
651
// Don't pretend to be the Red5 server
652
formatServer("Cygnal (0.8.6)");
654
// All HTTPServer messages are followed by a blank line.
657
// Add the binary blob for the header
660
// Make the result response, which is the 2nd data item passed in
661
// the request, a slash followed by a number like "/2".
663
result += "/onResult";
664
boost::shared_ptr<cygnal::Buffer> res = amf::AMF::encodeString(result);
665
_buffer.append(res->begin()+1, res->size()-1);
667
// Add the null data item
668
boost::shared_ptr<cygnal::Buffer> null = amf::AMF::encodeString("null");
669
_buffer.append(null->begin()+1, null->size()-1);
671
// Add the other binary blob
674
cygnal::Element::amf0_type_e type = static_cast<cygnal::Element::amf0_type_e>(*data);
675
if ((type == cygnal::Element::UNSUPPORTED_AMF0)
676
|| (type == cygnal::Element::NULL_AMF0)) {
678
// Red5 returns a NULL object when it's recieved an undefined one in the echo_test
679
} else if (type == cygnal::Element::UNDEFINED_AMF0) {
680
_buffer += cygnal::Element::NULL_AMF0;
682
// Add the AMF data we're echoing back
684
_buffer.append(data, size);
693
/// These methods extract data from an RTMPT message. RTMP is an
694
/// extension to HTTP that adds commands to manipulate the
695
/// connection's persistance.
697
/// The URL to be opened has the following form:
698
/// http://server/<comand>/[<client>/]<index>
700
/// denotes the RTMPT request type, "OPEN", "SEND", "IDLE", "CLOSE")
702
/// specifies the id of the client that performs the requests
703
/// (only sent for established sessions)
705
/// is a consecutive number that seems to be used to detect missing packages
707
HTTP::extractRTMPT(boost::uint8_t *data)
709
GNASH_REPORT_FUNCTION;
711
string body = reinterpret_cast<const char *>(data);
712
string tmp, cid, indx;
713
HTTP::rtmpt_cmd_e cmd;
715
// force the case to make comparisons easier
716
std::transform(body.begin(), body.end(), body.begin(),
717
(int(*)(int)) toupper);
718
string::size_type start, end;
720
// Extract the command first
721
start = body.find("OPEN", 0);
722
if (start != string::npos) {
725
start = body.find("SEND", 0);
726
if (start != string::npos) {
729
start = body.find("IDLE", 0);
730
if (start != string::npos) {
733
start = body.find("CLOSE", 0);
734
if (start != string::npos) {
738
// Extract the optional client id
739
start = body.find("/", start+1);
740
if (start != string::npos) {
741
end = body.find("/", start+1);
742
if (end != string::npos) {
743
indx = body.substr(end, body.size());
744
cid = body.substr(start, (end-start));
746
cid = body.substr(start, body.size());
750
_index = strtol(indx.c_str(), NULL, 0);
751
_clientid = strtol(cid.c_str(), NULL, 0);
752
end = body.find("\r\n", start);
753
// if (end != string::npos) {
754
// cmd = HTTP::CLOSE;
762
/// These methods extract data from an RTMPT message. RTMP is an
763
/// extension to HTTPServer that adds commands to manipulate the
764
/// connection's persistance.
766
/// The URL to be opened has the following form:
767
/// http://server/<comand>/[<client>/]<index>
769
/// denotes the RTMPT request type, "OPEN", "SEND", "IDLE", "CLOSE")
771
/// specifies the id of the client that performs the requests
772
/// (only sent for established sessions)
774
/// is a consecutive number that seems to be used to detect missing packages
775
HTTPServer::rtmpt_cmd_e
776
HTTPServer::extractRTMPT(boost::uint8_t *data)
778
GNASH_REPORT_FUNCTION;
780
string body = reinterpret_cast<const char *>(data);
781
string tmp, cid, indx;
782
HTTPServer::rtmpt_cmd_e cmd;
784
// force the case to make comparisons easier
785
std::transform(body.begin(), body.end(), body.begin(),
786
(int(*)(int)) toupper);
787
string::size_type start, end;
789
// Extract the command first
790
start = body.find("OPEN", 0);
791
if (start != string::npos) {
792
cmd = HTTPServer::OPEN;
794
start = body.find("SEND", 0);
795
if (start != string::npos) {
796
cmd = HTTPServer::SEND;
798
start = body.find("IDLE", 0);
799
if (start != string::npos) {
800
cmd = HTTPServer::IDLE;
802
start = body.find("CLOSE", 0);
803
if (start != string::npos) {
804
cmd = HTTPServer::CLOSE;
807
// Extract the optional client id
808
start = body.find("/", start+1);
809
if (start != string::npos) {
810
end = body.find("/", start+1);
811
if (end != string::npos) {
812
indx = body.substr(end, body.size());
813
cid = body.substr(start, (end-start));
815
cid = body.substr(start, body.size());
819
_index = strtol(indx.c_str(), NULL, 0);
820
_clientid = strtol(cid.c_str(), NULL, 0);
821
end = body.find("\r\n", start);
822
// if (end != string::npos) {
823
// cmd = HTTPServer::CLOSE;
830
HTTPServer::http_method_e
831
HTTPServer::extractCommand(boost::uint8_t *data)
833
GNASH_REPORT_FUNCTION;
835
// string body = reinterpret_cast<const char *>(data);
836
HTTPServer::http_method_e cmd = HTTP::HTTP_NONE;
838
// force the case to make comparisons easier
839
// std::transform(body.begin(), body.end(), body.begin(),
840
// (int(*)(int)) toupper);
842
// Extract the command
843
if (memcmp(data, "GET", 3) == 0) {
844
cmd = HTTP::HTTP_GET;
845
} else if (memcmp(data, "POST", 4) == 0) {
846
cmd = HTTP::HTTP_POST;
847
} else if (memcmp(data, "HEAD", 4) == 0) {
848
cmd = HTTP::HTTP_HEAD;
849
} else if (memcmp(data, "CONNECT", 7) == 0) {
850
cmd = HTTP::HTTP_CONNECT;
851
} else if (memcmp(data, "TRACE", 5) == 0) {
852
cmd = HTTP::HTTP_TRACE;
853
} else if (memcmp(data, "PUT", 3) == 0) {
854
cmd = HTTP::HTTP_PUT;
855
} else if (memcmp(data, "OPTIONS", 4) == 0) {
856
cmd = HTTP::HTTP_OPTIONS;
857
} else if (memcmp(data, "DELETE", 4) == 0) {
858
cmd = HTTP::HTTP_DELETE;
861
// For valid requests, the second argument, delimited by spaces
862
// is the filespec of the file being requested or transmitted.
863
if (cmd != HTTP::HTTP_NONE) {
864
boost::uint8_t *start = std::find(data, data+7, ' ') + 1;
865
boost::uint8_t *end = std::find(start + 2, data+PATH_MAX, ' ');
866
boost::uint8_t *params = std::find(start, end, '?');
868
_params = std::string(params+1, end);
869
_filespec = std::string(start, params);
870
log_debug("Parameters for file: \"%s\"", _params);
872
// This is fine as long as end is within the buffer.
873
_filespec = std::string(start, end);
875
// log_debug("Requesting file: \"%s\"", _filespec);
877
// The third field is always the HTTP version
878
// The version is the last field and is the protocol name
879
// followed by a slash, and the version number. Note that
880
// the version is not a double, even though it has a dot
881
// in it. It's actually two separate integers.
882
_version.major = *(end+6) - '0';
883
_version.minor = *(end+8) - '0';
884
// log_debug (_("Version: %d.%d"), _version.major, _version.minor);
891
HTTPServer::processHeaderFields(cygnal::Buffer &buf)
893
// GNASH_REPORT_FUNCTION;
894
string head(reinterpret_cast<const char *>(buf.reference()));
896
// The end of the header block is always followed by a blank line
897
string::size_type end = head.find("\r\n\r\n", 0);
898
// head.erase(end, buf.size()-end);
899
Tok t(head, Sep("\r\n"));
900
for (Tok::iterator i = t.begin(); i != t.end(); ++i) {
901
string::size_type pos = i->find(":", 0);
902
if (pos != string::npos) {
903
string name = i->substr(0, pos);
904
string value = i->substr(pos+2, i->size());
905
std::transform(name.begin(), name.end(), name.begin(),
906
(int(*)(int)) tolower);
907
std::transform(value.begin(), value.end(), value.begin(),
908
(int(*)(int)) tolower);
909
_fields[name] = value;
910
if (name == "keep-alive") {
912
if ((value != "on") && (value != "off")) {
913
_max_requests = strtol(value.c_str(), NULL, 0);
914
log_debug("Setting Max Requests for Keep-Alive to %d", _max_requests);
917
if (name == "connection") {
918
if (value.find("keep-alive", 0) != string::npos) {
922
if (name == "content-length") {
923
_filesize = strtol(value.c_str(), NULL, 0);
924
log_debug("Setting Content Length to %d", _filesize);
926
if (name == "content-type") {
927
// This is the type used by flash when sending a AMF data via POST
928
if (value == "application/x-amf") {
929
// log_debug("Got AMF data in the POST request!");
930
_filetype = DiskStream::FILETYPE_AMF;
932
// This is the type used by wget when sending a file via POST
933
if (value == "application/x-www-form-urlencoded") {
934
// log_debug("Got file data in the POST request");
935
_filetype = DiskStream::FILETYPE_ENCODED;
937
log_debug("Setting Content Type to %d", _filetype);
940
// cerr << "FIXME: " << (void *)i << " : " << dec << end << endl;
942
const boost::uint8_t *cmd = reinterpret_cast<const boost::uint8_t *>(i->c_str());
943
if (extractCommand(const_cast<boost::uint8_t *>(cmd)) == HTTP::HTTP_NONE) {
947
log_debug("Got a request, parsing \"%s\"", *i);
948
string::size_type start = i->find(" ");
949
string::size_type params = i->find("?");
950
string::size_type pos = i->find("HTTP/");
951
if (pos != string::npos) {
952
// The version is the last field and is the protocol name
953
// followed by a slash, and the version number. Note that
954
// the version is not a double, even though it has a dot
955
// in it. It's actually two separate integers.
956
_version.major = i->at(pos+5) - '0';
957
_version.minor = i->at(pos+7) - '0';
958
// log_debug (_("Version: %d.%d"), _version.major, _version.minor);
959
// the filespec in the request is the middle field, deliminated
960
// by a space on each end.
961
if (params != string::npos) {
962
_params = i->substr(params+1, end);
963
_filespec = i->substr(start+1, params);
964
log_debug("Parameters for file: \"%s\"", _params);
966
_filespec = i->substr(start+1, pos-start-2);
968
// log_debug("Requesting file: \"%s\"", _filespec);
970
// HTTP 1.1 enables persistant network connections
972
if (_version.minor > 0) {
973
log_debug("Enabling Keep Alive by default for HTTP > 1.0");
982
return buf.reference() + end + 4;
989
// GNASH_REPORT_FUNCTION;
996
HTTPServer::http_handler(Handler *hand, int netfd, cygnal::Buffer *buf)
998
GNASH_REPORT_FUNCTION;
1000
// Handler *hand = reinterpret_cast<Handler *>(args->handler);
1001
// cygnal::Buffer *buf = args->buffer;
1002
// boost::shared_ptr<HTTPServer> www(new HTTPServer); // = hand->getHTTPHandler (args->netfd);
1004
string url, parameters;
1005
// by default, only look once unless changed later
1006
// www->setDocRoot(crcfile.getDocumentRoot());
1007
// log_network("Docroot for HTTP files is %s", crcfile.getDocumentRoot());
1008
log_network("Processing HTTP data for fd #%d", netfd);
1010
// Wait for data, and when we get it, process it.
1011
#ifdef USE_STATISTICS
1012
struct timespec start;
1013
clock_gettime (CLOCK_REALTIME, &start);
1017
log_network("FIXME: Existing data in packet!");
1019
log_network("FIXME: No existing data in packet!");
1020
// See if we have any messages waiting
1021
if (recvMsg(netfd) == 0) {
1022
log_debug("Net HTTP server failed to read from fd #%d...", netfd);
1027
// Process incoming messages
1028
HTTP::http_method_e cmd = processClientRequest(hand, netfd, buf);
1029
if (cmd != HTTP::HTTP_GET) {
1030
log_debug("No active DiskStreams for fd #%d: %s...", netfd, _filespec);
1033
log_debug("Found active DiskStream! for fd #%d: %s", netfd, _filespec);
1034
hand->setDiskStream(netfd, _diskstream);
1035
cache.addFile(_filespec, _diskstream);
1036
// Send the first chunk of the file to the client.
1037
// log_network("Sending first chunk of %s", _filespec);
1038
_diskstream->play(netfd, false);
1043
if ((getField("content-type") == "application/x-amf")
1044
&& (getField("content-type") == "application/x-amf")
1045
&& (getFilespec() == "/echo/gateway")) {
1046
cerr << "GOT A GATEWAY REQUEST" << endl;
1050
string response = cache.findResponse(filestream->getFilespec());
1051
if (response.empty()) {
1052
cerr << "FIXME no cache hit for: " << www.getFilespec() << endl;
1053
// www.clearHeader();
1054
// cygnal::Buffer &ss = www.formatHeader(filestream->getFileSize(), HTTP::LIFE_IS_GOOD);
1055
// www.writeNet(args->netfd, (boost::uint8_t *)www.getHeader().c_str(), www.getHeader().size());
1056
// cache.addResponse(www.getFilespec(), www.getHeader());
1058
cerr << "FIXME cache hit on: " << www.getFilespec() << endl;
1059
www.writeNet(args->netfd, (boost::uint8_t *)response.c_str(), response.size());
1063
// Unless the Keep-Alive flag is set, this isn't a persisant network
1066
log_debug("Keep-Alive is off", keepAlive());
1068
log_debug("Keep-Alive is on", keepAlive());
1070
#ifdef USE_STATISTICS
1071
struct timespec end;
1072
clock_gettime (CLOCK_REALTIME, &end);
1073
log_debug("Processing time for GET request was %f seconds",
1074
static_cast<float>(((end.tv_sec - start.tv_sec) +
1075
((end.tv_nsec - start.tv_nsec)/1e9))));
1080
} // end of http_handler
1082
} // end of gnash namespace
1087
// indent-tabs-mode: t