Update with current status
[gnash.git] / cygnal / http_server.cpp
blob7b5f84a6653d9dfd772f901f402494cab16cf2ec
1 // http.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
5 //
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.
10 //
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
21 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h"
23 #endif
25 #include <mutex>
26 #include <boost/tokenizer.hpp>
27 #include <boost/date_time/posix_time/posix_time.hpp>
28 #include <boost/date_time/gregorian/gregorian.hpp>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <string>
33 #include <iostream>
34 #include <cstring>
35 #include <sstream>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <algorithm>
39 #include "GnashSystemIOHeaders.h" // read()
41 #include "amf.h"
42 #include "element.h"
43 #include "cque.h"
44 #include "log.h"
45 #include "crc.h"
46 #include "network.h"
47 //#include "handler.h"
48 #include "utility.h"
49 #include "buffer.h"
50 #include "http.h"
51 #include "diskstream.h"
53 // Cygnal specific headers
54 #include "http_server.h"
55 #include "proc.h"
56 #include "cache.h"
58 // Not POSIX, so best not rely on it if possible.
59 #ifndef PATH_MAX
60 # define PATH_MAX 1024
61 #endif
63 using namespace gnash;
64 using namespace std;
66 static std::mutex stl_mutex;
68 namespace cygnal
71 // The rcfile is loaded and parsed here:
72 static CRcInitFile& crcfile = CRcInitFile::getDefaultInstance();
73 static Cache& cache = Cache::getDefaultInstance();
74 // static Proc& cgis = Proc::getDefaultInstance();
76 HTTPServer::HTTPServer()
78 // GNASH_REPORT_FUNCTION;
81 HTTPServer::~HTTPServer()
83 // GNASH_REPORT_FUNCTION;
86 HTTP::http_method_e
87 HTTPServer::processClientRequest(int /* fd */)
89 GNASH_REPORT_FUNCTION;
91 //cygnal::Buffer *buf = new cygnal::Buffer;
92 // return processClientRequest(fd, buf);
94 return HTTP::HTTP_NONE;
97 HTTP::http_method_e
98 HTTPServer::processClientRequest(Handler *hand, int fd, cygnal::Buffer *buf)
100 GNASH_REPORT_FUNCTION;
102 cygnal::Buffer result;
104 if (buf) {
105 _cmd = extractCommand(buf->reference());
106 switch (_cmd) {
107 case HTTP::HTTP_GET:
108 result = processGetRequest(hand, fd, buf);
109 break;
110 case HTTP::HTTP_POST:
111 result = processPostRequest(fd, buf);
112 break;
113 case HTTP::HTTP_HEAD:
114 result = processHeadRequest(fd, buf);
115 break;
116 case HTTP::HTTP_CONNECT:
117 result = processConnectRequest(fd, buf);
118 break;
119 case HTTP::HTTP_TRACE:
120 result = processTraceRequest(fd, buf);
121 break;
122 case HTTP::HTTP_OPTIONS:
123 result = processOptionsRequest(fd, buf);
124 break;
125 case HTTP::HTTP_PUT:
126 result = processPutRequest(fd, buf);
127 break;
128 case HTTP::HTTP_DELETE:
129 result = processDeleteRequest(fd, buf);
130 break;
131 default:
132 break;
136 #if 0
138 writeNet(fd, result);
140 _docroot = crcfile.getDocumentRoot();
142 string url = _docroot + _filespec;
144 // See if the file is in the cache and already opened.
145 std::shared_ptr<DiskStream> filestream(cache.findFile(_filespec));
146 if (filestream) {
147 log_debug("FIXME: found filestream %s in cache!", _filespec);
148 filestream->dump();
149 } else {
150 filestream.reset(new DiskStream);
151 log_network(_("New filestream %s"), _filespec);
152 // cache.addFile(url, filestream); FIXME: always reload from disk for now.
154 // Oopen the file and read the first chunk into memory
155 if (filestream->open(url)) {
156 formatErrorResponse(HTTPServer::NOT_FOUND);
157 } else {
158 // Get the file size for the HTTPServer header
159 if (filestream->getFileType() == DiskStream::FILETYPE_NONE) {
160 formatErrorResponse(HTTPServer::NOT_FOUND);
161 } else {
162 // cache.addPath(_filespec, filestream->getFilespec());
163 // cache.addFile(_filespec, filestream);
166 // Close the file but leave resident for now.
167 if (filestream->fullyPopulated()) {
168 filestream->close();
170 // cache.addFile(_filespec, filestream);
172 #endif
174 return _cmd;
177 // A GET request asks the server to send a file to the client
178 cygnal::Buffer &
179 HTTPServer::processGetRequest(Handler *hand, int fd, cygnal::Buffer *buf)
181 GNASH_REPORT_FUNCTION;
183 // cerr << "QUE = " << _que.size() << endl;
185 // cerr << "YYYYYYY: " << (char *)buf->reference() << endl;
186 // cerr << hexify(buf->reference(), buf->allocated(), false) << endl;
188 if (buf == nullptr) {
189 // log_debug("Queue empty, net connection dropped for fd #%d", getFileFd());
190 log_debug("Queue empty, net connection dropped for fd #%d", fd);
191 // cygnal::Buffer buf;
192 return _buf;
195 clearHeader();
196 processHeaderFields(buf);
198 _docroot = crcfile.getDocumentRoot();
200 string url = _docroot + _filespec;
202 std::shared_ptr<DiskStream> ds = hand->getDiskStream(fd);
203 if (ds) {
204 _diskstream = ds;
206 if (!_diskstream) {
207 _diskstream.reset(new DiskStream);
208 log_network(_("New filestream %s"), _filespec);
209 } else {
210 log_network(_("Reusing filestream %s"), _filespec);
213 // Oopen the file and read the first chunk into memory
214 if (_diskstream->open(url)) {
215 formatErrorResponse(HTTPServer::NOT_FOUND);
216 } else {
217 // Get the file size for the HTTPServer header
218 if (_diskstream->getFileType() == DiskStream::FILETYPE_NONE) {
219 formatErrorResponse(HTTPServer::NOT_FOUND);
220 } else {
221 // cache.addPath(_filespec, filestream->getFilespec());
222 // cache.addFile(_filespec, filestream);
225 // Closing the file closes the disk file, but leaves data resident
226 // in memory for future access to this file. If we've been opened,
227 // the next operation is to start writing the file next time
228 // ::play() is called.
229 if (_diskstream->fullyPopulated()) {
230 _diskstream->close();
232 _diskstream->setState(DiskStream::PLAY);
233 // cache.addFile(_filespec, _diskstream);
235 // Create the reply message
236 // _close = true; Force sending the close connection in the header
237 cygnal::Buffer &reply = formatHeader(_diskstream->getFileType(),
238 _diskstream->getFileSize(),
239 HTTPServer::OK);
241 writeNet(fd, reply);
243 size_t filesize = _diskstream->getFileSize();
244 // size_t bytes_read = 0;
245 // int ret;
246 // size_t page = 0;
247 if (filesize) {
248 #ifdef USE_STATS_CACHE
249 struct timespec start;
250 clock_gettime (CLOCK_REALTIME, &start);
251 #endif
253 #ifdef USE_STATS_CACHE
254 struct timespec end;
255 clock_gettime (CLOCK_REALTIME, &end);
256 double time = (end.tv_sec - start.tv_sec) + ((end.tv_nsec - start.tv_nsec)/1e9);
257 ios::fmtflags f(cerr.flags());
258 cerr << "File " << _filespec
259 << " transferred " << filesize << " bytes in: " << fixed
260 << time << " seconds for net fd #" << fd << endl;
261 cerr.flags(f);
262 #endif
265 return reply;
268 // A POST request asks sends a data from the client to the server. After processing
269 // the header like we normally do, we then read the amount of bytes specified by
270 // the "content-length" field, and then write that data to disk, or decode the amf.
271 std::shared_ptr<cygnal::Buffer>
272 HTTPServer::processPostRequest(int fd, cygnal::Buffer * /* bufFIXME */)
274 GNASH_REPORT_FUNCTION;
276 // cerr << "QUE1 = " << _que.size() << endl;
278 std::shared_ptr<cygnal::Buffer> buf;
280 if (_que.size() == 0) {
281 return buf;
284 buf = _que.pop();
285 if (buf == nullptr) {
286 log_debug("Queue empty, net connection dropped for fd #%d",
287 getFileFd());
288 return buf;
290 // cerr << __FUNCTION__ << buf->allocated() << " : " << hexify(buf->reference(), buf->allocated(), true) << endl;
292 clearHeader();
293 std::uint8_t *data = processHeaderFields(buf.get());
294 size_t length = strtol(getField("content-length").c_str(), nullptr, 0);
295 std::shared_ptr<cygnal::Buffer> content(new cygnal::Buffer(length));
296 int ret = 0;
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);
300 ret = length;
301 } else {
302 // cerr << "Need to read more data, only have " << buf->allocated() << " bytes" << endl;
303 ret = readNet(fd, *content, 2);
304 if (ret < 0) {
305 log_error(_("couldn't read data!"));
307 data = content->reference();
310 if (getField("content-type") == "application/x-www-form-urlencoded") {
311 log_debug("Got file data in POST");
312 string url = _docroot + _filespec;
313 DiskStream ds(url, *content);
314 ds.writeToDisk();
315 // ds.close();
316 // oh boy, we got ourselves some encoded AMF objects instead of a boring file.
317 } else if (getField("content-type") == "application/x-amf") {
318 log_debug("Got AMF data in POST");
319 #if 0
320 amf::AMF amf;
321 std::shared_ptr<cygnal::Element> el = amf.extractAMF(content.reference(), content.end());
322 el->dump(); // FIXME: do something intelligent
323 // with this Element
324 #endif
327 // Send the reply
329 // NOTE: this is a "special" path we trap until we have real CGI support
330 if ((getField("content-type") == "application/x-amf")
331 && (getField("content-type") == "application/x-amf")) {
332 #ifdef USE_CGIBIN
333 if (_filespec == "/echo/gateway") {
336 Proc cgis;
337 string path = _docroot;
338 path += _filespec;
339 cgis.startCGI(_filespec, true, CGIBIN_PORT);
340 cgis.createClient("localhost", CGIBIN_PORT);
341 cgis.writeNet(*content);
342 std::shared_ptr<cygnal::Buffer> reply = cgis.readNet();
344 writeNet(fd, *reply);
345 // cgis.stopCGI(_filespec);
346 #else
347 vector<std::shared_ptr<cygnal::Element> > headers = parseEchoRequest(*content);
348 //std::shared_ptr<cygnal::Element> &el0 = headers[0];
350 if (headers.size() >= 4) {
351 if (headers[3]) {
352 cygnal::Buffer &reply = formatEchoResponse(headers[1]->getName(), *headers[3]);
353 // cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), true) << endl;
354 // cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), false) << endl;
355 writeNet(fd, reply);
358 #endif
359 } else {
360 cygnal::Buffer &reply = formatHeader(_filetype, _filesize, HTTPServer::OK);
361 writeNet(fd, reply);
364 return buf;
367 std::shared_ptr<cygnal::Buffer>
368 HTTPServer::processPutRequest(int /* fd */, cygnal::Buffer */* buf */)
370 std::shared_ptr<cygnal::Buffer> buf;
371 // GNASH_REPORT_FUNCTION;
372 log_unimpl(_("PUT request"));
374 return buf;
377 std::shared_ptr<cygnal::Buffer>
378 HTTPServer::processDeleteRequest(int /* fd */, cygnal::Buffer */* buf */)
380 // GNASH_REPORT_FUNCTION;
381 std::shared_ptr<cygnal::Buffer> buf;
382 log_unimpl(_("DELETE request"));
384 return buf;
387 std::shared_ptr<cygnal::Buffer>
388 HTTPServer::processConnectRequest(int /* fd */, cygnal::Buffer */* buf */)
390 // GNASH_REPORT_FUNCTION;
391 std::shared_ptr<cygnal::Buffer> buf;
392 log_unimpl(_("CONNECT request"));
394 return buf;
397 std::shared_ptr<cygnal::Buffer>
398 HTTPServer::processOptionsRequest(int /* fd */, cygnal::Buffer */* buf */)
400 // GNASH_REPORT_FUNCTION;
401 std::shared_ptr<cygnal::Buffer> buf;
402 log_unimpl(_("OPTIONS request"));
404 return buf;
407 std::shared_ptr<cygnal::Buffer>
408 HTTPServer::processHeadRequest(int /* fd */, cygnal::Buffer */* buf */)
410 // GNASH_REPORT_FUNCTION;
411 std::shared_ptr<cygnal::Buffer> buf;
412 log_unimpl(_("HEAD request"));
414 return buf;
417 std::shared_ptr<cygnal::Buffer>
418 HTTPServer::processTraceRequest(int /* fd */, cygnal::Buffer */* buf */)
420 // GNASH_REPORT_FUNCTION;
421 std::shared_ptr<cygnal::Buffer> buf;
422 log_unimpl(_("TRACE request"));
424 return buf;
427 cygnal::Buffer &
428 HTTPServer::formatErrorResponse(http_status_e code)
430 // GNASH_REPORT_FUNCTION;
432 char num[12];
433 // First build the message body, so we know how to set Content-Length
434 _buffer += "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n";
435 _buffer += "<html><head>\r\n";
436 _buffer += "<title>";
437 sprintf(num, "%d", code);
438 _buffer += num;
439 _buffer += " Not Found</title>\r\n";
440 _buffer += "</head><body>\r\n";
441 _buffer += "<h1>Not Found</h1>\r\n";
442 _buffer += "<p>The requested URL ";
443 _buffer += _filespec;
444 _buffer += " was not found on this server.</p>\r\n";
445 _buffer += "<hr>\r\n";
446 _buffer += "<address>Cygnal (GNU/Linux) Server at ";
447 _buffer += getField("host");
448 _buffer += " </address>\r\n";
449 _buffer += "</body></html>\r\n";
451 // First build the header
452 formatDate();
453 formatServer();
454 formatContentLength(_filesize);
455 formatConnection("close");
456 formatContentType(_filetype);
458 // All HTTPServer messages are followed by a blank line.
459 terminateHeader();
461 return _buffer;
464 cygnal::Buffer &
465 HTTPServer::formatGetReply(http_status_e code)
468 // GNASH_REPORT_FUNCTION;
470 return formatHeader(_filesize, code);
473 cygnal::Buffer &
474 HTTPServer::formatGetReply(size_t size, http_status_e code)
476 // GNASH_REPORT_FUNCTION;
478 formatHeader(size, code);
480 // int ret = Network::writeNet(_header.str());
481 // std::uint8_t *ptr = (std::uint8_t *)_body.str().c_str();
482 // buf->copy(ptr, _body.str().size());
483 // _handler->dump();
485 #if 0
486 if (_header.str().size()) {
487 log_debug ("Sent GET Reply");
488 return _buffer;
489 } else {
490 clearHeader();
491 log_debug ("Couldn't send GET Reply, no header data");
493 #endif
495 return _buffer;
498 cygnal::Buffer &
499 HTTPServer::formatPostReply(rtmpt_cmd_e /* code */)
501 GNASH_REPORT_FUNCTION;
503 formatDate();
504 formatServer();
505 formatContentType(DiskStream::FILETYPE_AMF);
506 // All HTTPServer messages are followed by a blank line.
507 terminateHeader();
508 return _buffer;
510 #if 0
511 formatHeader(_filesize, code);
512 std::shared_ptr<cygnal::Buffer> buf = new cygnal::Buffer;
513 if (_header.str().size()) {
514 buf->resize(_header.str().size());
515 string str = _header.str();
516 buf->copy(str);
517 _handler->pushout(buf);
518 _handler->notifyout();
519 log_debug ("Sent GET Reply");
520 return true; // Default to true
521 } else {
522 clearHeader();
523 log_debug ("Couldn't send POST Reply, no header data");
525 #endif
527 return _buffer;
530 #ifndef USE_CGIBIN
531 // Parse an Echo Request message coming from the Red5 echo_test. This
532 // method should only be used for testing purposes.
533 vector<std::shared_ptr<cygnal::Element > >
534 HTTPServer::parseEchoRequest(std::uint8_t *data, size_t size)
536 // GNASH_REPORT_FUNCTION;
538 vector<std::shared_ptr<cygnal::Element > > headers;
540 // skip past the header bytes, we don't care about them.
541 std::uint8_t *tmpptr = data + 6;
543 std::uint16_t length;
544 length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
545 tmpptr += sizeof(std::uint16_t);
547 // Get the first name, which is a raw string, and not preceded by
548 // a type byte.
549 std::shared_ptr<cygnal::Element > el1(new cygnal::Element);
551 // If the length of the name field is corrupted, then we get out of
552 // range quick, and corrupt memory. This is a bit of a hack, but
553 // reduces memory errors caused by some of the corrupted tes cases.
554 std::uint8_t *endstr = std::find(tmpptr, tmpptr+length, '\0');
555 if (endstr != tmpptr+length) {
556 log_debug("Caught corrupted string! length was %d, null at %d",
557 length, endstr-tmpptr);
558 length = endstr-tmpptr;
560 el1->setName(tmpptr, length);
561 tmpptr += length;
562 headers.push_back(el1);
564 // Get the second name, which is a raw string, and not preceded by
565 // a type byte.
566 length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
567 tmpptr += sizeof(std::uint16_t);
568 std::shared_ptr<cygnal::Element > el2(new cygnal::Element);
570 // std::string name2(reinterpret_cast<const char *>(tmpptr), length);
571 // el2->setName(name2.c_str(), name2.size());
572 // If the length of the name field is corrupted, then we get out of
573 // range quick, and corrupt memory. This is a bit of a hack, but
574 // reduces memory errors caused by some of the corrupted tes cases.
575 endstr = std::find(tmpptr, tmpptr+length, '\0');
576 if (endstr != tmpptr+length) {
577 log_debug("Caught corrupted string! length was %d, null at %d",
578 length, endstr-tmpptr);
579 length = endstr-tmpptr;
581 el2->setName(tmpptr, length);
582 headers.push_back(el2);
583 tmpptr += length;
585 // Get the last two pieces of data, which are both AMF encoded
586 // with a type byte.
587 amf::AMF amf;
588 std::shared_ptr<cygnal::Element> el3 = amf.extractAMF(tmpptr, tmpptr + size);
589 headers.push_back(el3);
590 tmpptr += amf.totalsize();
592 std::shared_ptr<cygnal::Element> el4 = amf.extractAMF(tmpptr, tmpptr + size);
593 headers.push_back(el4);
595 return headers;
598 // format a response to the 'echo' test used for testing Gnash. This
599 // is only used for testing by developers. The format appears to be
600 // two strings, followed by a double, followed by the "onResult".
601 cygnal::Buffer &
602 HTTPServer::formatEchoResponse(const std::string &num, cygnal::Element &el)
604 // GNASH_REPORT_FUNCTION;
605 std::shared_ptr<cygnal::Buffer> data;
607 cygnal::Element nel;
608 if (el.getType() == cygnal::Element::TYPED_OBJECT_AMF0) {
609 nel.makeTypedObject();
610 string name = el.getName();
611 nel.setName(name);
612 if (el.propertySize()) {
613 // FIXME: see about using std::reverse() instead.
614 for (int i=el.propertySize()-1; i>=0; i--) {
615 // for (int i=0 ; i<el.propertySize(); i++) {
616 std::shared_ptr<cygnal::Element> child = el.getProperty(i);
617 nel.addProperty(child);
619 data = nel.encode();
620 } else {
621 data = el.encode();
623 } else {
624 data = el.encode();
627 return formatEchoResponse(num, data->reference(), data->allocated());
630 cygnal::Buffer &
631 HTTPServer::formatEchoResponse(const std::string &num, cygnal::Buffer &data)
633 // GNASH_REPORT_FUNCTION;
634 return formatEchoResponse(num, data.reference(), data.allocated());
637 cygnal::Buffer &
638 HTTPServer::formatEchoResponse(const std::string &num, std::uint8_t *data, size_t size)
640 // GNASH_REPORT_FUNCTION;
642 //std::uint8_t *tmpptr = data;
644 // FIXME: temporary hacks while debugging
645 cygnal::Buffer fixme("00 00 00 00 00 01");
646 cygnal::Buffer fixme2("ff ff ff ff");
648 _buffer = "HTTPServer/1.1 200 OK\r\n";
649 formatContentType(DiskStream::FILETYPE_AMF);
650 // formatContentLength(size);
651 // FIXME: this is a hack ! Calculate a real size!
652 formatContentLength(size+29);
654 // Don't pretend to be the Red5 server
655 formatServer("Cygnal (0.8.6)");
657 // All HTTPServer messages are followed by a blank line.
658 terminateHeader();
660 // Add the binary blob for the header
661 _buffer += fixme;
663 // Make the result response, which is the 2nd data item passed in
664 // the request, a slash followed by a number like "/2".
665 string result = num;
666 result += "/onResult";
667 std::shared_ptr<cygnal::Buffer> res = amf::AMF::encodeString(result);
668 _buffer.append(res->begin()+1, res->size()-1);
670 // Add the null data item
671 std::shared_ptr<cygnal::Buffer> null = amf::AMF::encodeString("null");
672 _buffer.append(null->begin()+1, null->size()-1);
674 // Add the other binary blob
675 _buffer += fixme2;
677 cygnal::Element::amf0_type_e type = static_cast<cygnal::Element::amf0_type_e>(*data);
678 if ((type == cygnal::Element::UNSUPPORTED_AMF0)
679 || (type == cygnal::Element::NULL_AMF0)) {
680 _buffer += type;
681 // Red5 returns a NULL object when it's recieved an undefined one in the echo_test
682 } else if (type == cygnal::Element::UNDEFINED_AMF0) {
683 _buffer += cygnal::Element::NULL_AMF0;
684 } else {
685 // Add the AMF data we're echoing back
686 if (size) {
687 _buffer.append(data, size);
691 return _buffer;
693 #endif
695 #if 0
696 /// These methods extract data from an RTMPT message. RTMP is an
697 /// extension to HTTP that adds commands to manipulate the
698 /// connection's persistance.
700 /// The URL to be opened has the following form:
701 /// http://server/<comand>/[<client>/]<index>
702 /// <command>
703 /// denotes the RTMPT request type, "OPEN", "SEND", "IDLE", "CLOSE")
704 /// <client>
705 /// specifies the id of the client that performs the requests
706 /// (only sent for established sessions)
707 /// <index>
708 /// is a consecutive number that seems to be used to detect missing packages
709 HTTP::rtmpt_cmd_e
710 HTTP::extractRTMPT(std::uint8_t *data)
712 GNASH_REPORT_FUNCTION;
714 string body = reinterpret_cast<const char *>(data);
715 string tmp, cid, indx;
716 HTTP::rtmpt_cmd_e cmd;
718 // force the case to make comparisons easier
719 std::transform(body.begin(), body.end(), body.begin(),
720 (int(*)(int)) toupper);
721 string::size_type start, end;
723 // Extract the command first
724 start = body.find("OPEN", 0);
725 if (start != string::npos) {
726 cmd = HTTP::OPEN;
728 start = body.find("SEND", 0);
729 if (start != string::npos) {
730 cmd = HTTP::SEND;
732 start = body.find("IDLE", 0);
733 if (start != string::npos) {
734 cmd = HTTP::IDLE;
736 start = body.find("CLOSE", 0);
737 if (start != string::npos) {
738 cmd = HTTP::CLOSE;
741 // Extract the optional client id
742 start = body.find("/", start+1);
743 if (start != string::npos) {
744 end = body.find("/", start+1);
745 if (end != string::npos) {
746 indx = body.substr(end, body.size());
747 cid = body.substr(start, (end-start));
748 } else {
749 cid = body.substr(start, body.size());
753 _index = strtol(indx.c_str(), NULL, 0);
754 _clientid = strtol(cid.c_str(), NULL, 0);
755 end = body.find("\r\n", start);
756 // if (end != string::npos) {
757 // cmd = HTTP::CLOSE;
758 // }
760 return cmd;
763 #endif
765 /// These methods extract data from an RTMPT message. RTMP is an
766 /// extension to HTTPServer that adds commands to manipulate the
767 /// connection's persistance.
769 /// The URL to be opened has the following form:
770 /// http://server/<comand>/[<client>/]<index>
771 /// <command>
772 /// denotes the RTMPT request type, "OPEN", "SEND", "IDLE", "CLOSE")
773 /// <client>
774 /// specifies the id of the client that performs the requests
775 /// (only sent for established sessions)
776 /// <index>
777 /// is a consecutive number that seems to be used to detect missing packages
778 HTTPServer::rtmpt_cmd_e
779 HTTPServer::extractRTMPT(std::uint8_t *data)
781 GNASH_REPORT_FUNCTION;
783 string body = reinterpret_cast<const char *>(data);
784 string tmp, cid, indx;
785 HTTPServer::rtmpt_cmd_e cmd;
787 // force the case to make comparisons easier
788 std::transform(body.begin(), body.end(), body.begin(),
789 (int(*)(int)) toupper);
790 string::size_type start, end;
792 // Extract the command first
793 start = body.find("OPEN", 0);
794 if (start != string::npos) {
795 cmd = HTTPServer::OPEN;
797 start = body.find("SEND", 0);
798 if (start != string::npos) {
799 cmd = HTTPServer::SEND;
801 start = body.find("IDLE", 0);
802 if (start != string::npos) {
803 cmd = HTTPServer::IDLE;
805 start = body.find("CLOSE", 0);
806 if (start != string::npos) {
807 cmd = HTTPServer::CLOSE;
810 // Extract the optional client id
811 start = body.find("/", start+1);
812 if (start != string::npos) {
813 end = body.find("/", start+1);
814 if (end != string::npos) {
815 indx = body.substr(end, body.size());
816 cid = body.substr(start, (end-start));
817 } else {
818 cid = body.substr(start, body.size());
822 _index = strtol(indx.c_str(), nullptr, 0);
823 _clientid = strtol(cid.c_str(), nullptr, 0);
824 end = body.find("\r\n", start);
825 // if (end != string::npos) {
826 // cmd = HTTPServer::CLOSE;
827 // }
829 return cmd;
832 #if 0
833 HTTPServer::http_method_e
834 HTTPServer::extractCommand(std::uint8_t *data)
836 GNASH_REPORT_FUNCTION;
838 // string body = reinterpret_cast<const char *>(data);
839 HTTPServer::http_method_e cmd = HTTP::HTTP_NONE;
841 // force the case to make comparisons easier
842 // std::transform(body.begin(), body.end(), body.begin(),
843 // (int(*)(int)) toupper);
845 // Extract the command
846 if (memcmp(data, "GET", 3) == 0) {
847 cmd = HTTP::HTTP_GET;
848 } else if (memcmp(data, "POST", 4) == 0) {
849 cmd = HTTP::HTTP_POST;
850 } else if (memcmp(data, "HEAD", 4) == 0) {
851 cmd = HTTP::HTTP_HEAD;
852 } else if (memcmp(data, "CONNECT", 7) == 0) {
853 cmd = HTTP::HTTP_CONNECT;
854 } else if (memcmp(data, "TRACE", 5) == 0) {
855 cmd = HTTP::HTTP_TRACE;
856 } else if (memcmp(data, "PUT", 3) == 0) {
857 cmd = HTTP::HTTP_PUT;
858 } else if (memcmp(data, "OPTIONS", 4) == 0) {
859 cmd = HTTP::HTTP_OPTIONS;
860 } else if (memcmp(data, "DELETE", 4) == 0) {
861 cmd = HTTP::HTTP_DELETE;
864 // For valid requests, the second argument, delimited by spaces
865 // is the filespec of the file being requested or transmitted.
866 if (cmd != HTTP::HTTP_NONE) {
867 std::uint8_t *start = std::find(data, data+7, ' ') + 1;
868 std::uint8_t *end = std::find(start + 2, data+PATH_MAX, ' ');
869 std::uint8_t *params = std::find(start, end, '?');
870 if (params != end) {
871 _params = std::string(params+1, end);
872 _filespec = std::string(start, params);
873 log_debug("Parameters for file: \"%s\"", _params);
874 } else {
875 // This is fine as long as end is within the buffer.
876 _filespec = std::string(start, end);
878 // log_debug("Requesting file: \"%s\"", _filespec);
880 // The third field is always the HTTP version
881 // The version is the last field and is the protocol name
882 // followed by a slash, and the version number. Note that
883 // the version is not a double, even though it has a dot
884 // in it. It's actually two separate integers.
885 _version.major = *(end+6) - '0';
886 _version.minor = *(end+8) - '0';
887 // log_debug("Version: %d.%d", _version.major, _version.minor);
890 return cmd;
893 std::uint8_t *
894 HTTPServer::processHeaderFields(cygnal::Buffer &buf)
896 // GNASH_REPORT_FUNCTION;
897 string head(reinterpret_cast<const char *>(buf.reference()));
899 // The end of the header block is always followed by a blank line
900 string::size_type end = head.find("\r\n\r\n", 0);
901 // head.erase(end, buf.size()-end);
902 Tok t(head, Sep("\r\n"));
903 for (Tok::iterator i = t.begin(); i != t.end(); ++i) {
904 string::size_type pos = i->find(":", 0);
905 if (pos != string::npos) {
906 string name = i->substr(0, pos);
907 string value = i->substr(pos+2, i->size());
908 std::transform(name.begin(), name.end(), name.begin(),
909 (int(*)(int)) tolower);
910 std::transform(value.begin(), value.end(), value.begin(),
911 (int(*)(int)) tolower);
912 _fields[name] = value;
913 if (name == "keep-alive") {
914 _keepalive = true;
915 if ((value != "on") && (value != "off")) {
916 _max_requests = strtol(value.c_str(), NULL, 0);
917 log_debug("Setting Max Requests for Keep-Alive to %d",
918 _max_requests);
921 if (name == "connection") {
922 if (value.find("keep-alive", 0) != string::npos) {
923 _keepalive = true;
926 if (name == "content-length") {
927 _filesize = strtol(value.c_str(), NULL, 0);
928 log_debug("Setting Content Length to %d", _filesize);
930 if (name == "content-type") {
931 // This is the type used by flash when sending a AMF data via POST
932 if (value == "application/x-amf") {
933 // log_debug("Got AMF data in the POST request!");
934 _filetype = DiskStream::FILETYPE_AMF;
936 // This is the type used by wget when sending a file via POST
937 if (value == "application/x-www-form-urlencoded") {
938 // log_debug("Got file data in the POST request");
939 _filetype = DiskStream::FILETYPE_ENCODED;
941 log_debug("Setting Content Type to %d", _filetype);
944 // cerr << "FIXME: " << (void *)i << " : " << dec << end << endl;
945 } else {
946 const std::uint8_t *cmd = reinterpret_cast<const std::uint8_t *>(i->c_str());
947 if (extractCommand(const_cast<std::uint8_t *>(cmd)) == HTTP::HTTP_NONE) {
948 break;
949 #if 1
950 } else {
951 log_debug("Got a request, parsing \"%s\"", *i);
952 string::size_type start = i->find(" ");
953 string::size_type params = i->find("?");
954 string::size_type pos = i->find("HTTP/");
955 if (pos != string::npos) {
956 // The version is the last field and is the protocol name
957 // followed by a slash, and the version number. Note that
958 // the version is not a double, even though it has a dot
959 // in it. It's actually two separate integers.
960 _version.major = i->at(pos+5) - '0';
961 _version.minor = i->at(pos+7) - '0';
962 // log_debug ("Version: %d.%d", _version.major, _version.minor);
963 // the filespec in the request is the middle field, deliminated
964 // by a space on each end.
965 if (params != string::npos) {
966 _params = i->substr(params+1, end);
967 _filespec = i->substr(start+1, params);
968 log_debug("Parameters for file: \"%s\"", _params);
969 } else {
970 _filespec = i->substr(start+1, pos-start-2);
972 // log_debug("Requesting file: \"%s\"", _filespec);
974 // HTTP 1.1 enables persistent network connections
975 // by default.
976 if (_version.minor > 0) {
977 log_debug("Enabling Keep Alive by default for HTTP > 1.0");
978 _keepalive = true;
982 #endif
986 return buf.reference() + end + 4;
988 #endif
990 void
991 HTTPServer::dump()
993 // GNASH_REPORT_FUNCTION;
994 if (_diskstream) {
995 _diskstream->dump();
999 bool
1000 HTTPServer::http_handler(Handler *hand, int netfd, cygnal::Buffer *buf)
1002 GNASH_REPORT_FUNCTION;
1004 // Handler *hand = reinterpret_cast<Handler *>(args->handler);
1005 // cygnal::Buffer *buf = args->buffer;
1006 // std::shared_ptr<HTTPServer> www(new HTTPServer); // = hand->getHTTPHandler (args->netfd);
1008 string url, parameters;
1009 // by default, only look once unless changed later
1010 // www->setDocRoot(crcfile.getDocumentRoot());
1011 // log_network("Docroot for HTTP files is %s", crcfile.getDocumentRoot());
1012 log_network(_("Processing HTTP data for fd #%d"), netfd);
1014 // Wait for data, and when we get it, process it.
1015 #ifdef USE_STATISTICS
1016 struct timespec start;
1017 clock_gettime (CLOCK_REALTIME, &start);
1018 #endif
1020 if (buf) {
1021 log_network(_("FIXME: Existing data in packet!"));
1022 } else {
1023 log_network(_("FIXME: No existing data in packet!"));
1024 // See if we have any messages waiting
1025 if (recvMsg(netfd) == 0) {
1026 log_debug("Net HTTP server failed to read from fd #%d...", netfd);
1027 return false;
1031 // Process incoming messages
1032 HTTP::http_method_e cmd = processClientRequest(hand, netfd, buf);
1033 if (cmd != HTTP::HTTP_GET) {
1034 log_debug("No active DiskStreams for fd #%d: %s...", netfd,
1035 _filespec);
1036 } else {
1037 if (_diskstream) {
1038 log_debug("Found active DiskStream! for fd #%d: %s", netfd,
1039 _filespec);
1040 hand->setDiskStream(netfd, _diskstream);
1041 cache.addFile(_filespec, _diskstream);
1042 // Send the first chunk of the file to the client.
1043 // log_network("Sending first chunk of %s", _filespec);
1044 _diskstream->play(netfd, false);
1048 // www->dump();
1049 if ((getField("content-type") == "application/x-amf")
1050 && (getField("content-type") == "application/x-amf")
1051 && (getFilespec() == "/echo/gateway")) {
1052 cerr << "GOT A GATEWAY REQUEST" << endl;
1055 #if 0
1056 string response = cache.findResponse(filestream->getFilespec());
1057 if (response.empty()) {
1058 cerr << "FIXME no cache hit for: " << www.getFilespec() << endl;
1059 // www.clearHeader();
1060 // cygnal::Buffer &ss = www.formatHeader(filestream->getFileSize(), HTTP::LIFE_IS_GOOD);
1061 // www.writeNet(args->netfd, (std::uint8_t *)www.getHeader().c_str(), www.getHeader().size());
1062 // cache.addResponse(www.getFilespec(), www.getHeader());
1063 } else {
1064 cerr << "FIXME cache hit on: " << www.getFilespec() << endl;
1065 www.writeNet(args->netfd, (std::uint8_t *)response.c_str(), response.size());
1067 #endif
1069 // Unless the Keep-Alive flag is set, this isn't a persisant network
1070 // connection.
1071 if (!keepAlive()) {
1072 log_debug("Keep-Alive is off", keepAlive());
1073 } else {
1074 log_debug("Keep-Alive is on", keepAlive());
1076 #ifdef USE_STATISTICS
1077 struct timespec end;
1078 clock_gettime (CLOCK_REALTIME, &end);
1079 log_debug("Processing time for GET request was %f seconds",
1080 static_cast<float>(((end.tv_sec - start.tv_sec) +
1081 ((end.tv_nsec - start.tv_nsec)/1e9))));
1082 #endif
1084 return keepAlive();
1086 } // end of http_handler
1088 } // end of gnash namespace
1091 // local Variables:
1092 // mode: C++
1093 // indent-tabs-mode: t
1094 // End: