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.
15 //
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
19 //
20
21 #include <mutex>
22 #include <boost/tokenizer.hpp>
23 #include <boost/date_time/posix_time/posix_time.hpp>
24 #include <boost/date_time/gregorian/gregorian.hpp>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <string>
29 #include <iostream>
30 #include <cstring>
31 #include <sstream>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <algorithm>
35
36 #include "GnashSystemIOHeaders.h" // read()
37 #include "http.h"
38 #include "amf.h"
39 #include "element.h"
40 #include "cque.h"
41 #include "log.h"
42 #include "network.h"
43 // #include "handler.h"
44 #include "utility.h"
45 #include "buffer.h"
46 #include "diskstream.h"
47 #include "cache.h"
48
49 // Not POSIX, so best not rely on it if possible.
50 #ifndef PATH_MAX
51 # define PATH_MAX 1024
52 #endif
53
54 #if defined(_WIN32) || defined(WIN32)
55 # define __PRETTY_FUNCTION__ __FUNCDNAME__
56 # include <winsock2.h>
57 # include <direct.h>
58 #else
59 # include <unistd.h>
60 # include <sys/param.h>
61 #endif
62
63 using std::string;
64
65 static std::mutex stl_mutex;
66
67 namespace gnash
68 {
69
70 // extern map<int, Handler *> handlers;
71
72 #if 0
73 // FIXME, this seems too small to me. --gnu
74 static const int readsize = 1024;
75 #endif
76
77 static Cache& cache = Cache::getDefaultInstance();
78
HTTP()79 HTTP::HTTP()
80 : _filetype(DiskStream::FILETYPE_HTML),
81 _filesize(0),
82 _keepalive(false),
83 // _handler(0),
84 _clientid(0),
85 _index(0),
86 _max_requests(0),
87 _close(false)
88 {
89 // GNASH_REPORT_FUNCTION;
90 // struct status_codes *status = new struct status_codes;
91 _version.major = 0;
92 _version.minor = 0;
93
94 // _status_codes(CONTINUE, status);
95 }
96
97 #if 0
98 HTTP::HTTP(Handler *hand)
99 : _filetype(DiskStream::FILETYPE_HTML),
100 _filesize(0),
101 _keepalive(false),
102 _clientid(0),
103 _index(0),
104 _max_requests(0),
105 _close(false)
106
107 {
108 // GNASH_REPORT_FUNCTION;
109 // _handler = hand;
110 _version.major = 0;
111 _version.minor = 0;
112 }
113 #endif
114
~HTTP()115 HTTP::~HTTP()
116 {
117 // GNASH_REPORT_FUNCTION;
118 }
119
120 bool
clearHeader()121 HTTP::clearHeader()
122 {
123 // _header.str("");
124 _buffer.clear();
125 _filesize = 0;
126 _max_requests = 0;
127
128 return true;
129 }
130
131 HTTP &
operator =(HTTP &)132 HTTP::operator = (HTTP& /*obj*/)
133 {
134 GNASH_REPORT_FUNCTION;
135 // this = obj;
136 // TODO: FIXME !
137 return *this;
138 }
139
140
141 std::uint8_t *
processHeaderFields(cygnal::Buffer * buf)142 HTTP::processHeaderFields(cygnal::Buffer *buf)
143 {
144 // GNASH_REPORT_FUNCTION;
145 string head(reinterpret_cast<const char *>(buf->reference()), buf->size());
146
147 // The end of the header block is always followed by a blank line
148 string::size_type end = head.find("\r\n\r\n", 0);
149 // head.erase(end, buf.size()-end);
150 Tok t(head, Sep("\r\n"));
151 for (Tok::iterator i = t.begin(); i != t.end(); ++i) {
152 string::size_type pos = i->find(":", 0);
153 if (pos != string::npos) {
154 string name = i->substr(0, pos);
155 string value = i->substr(pos+2, i->size());
156 std::transform(name.begin(), name.end(), name.begin(),
157 (int(*)(int)) tolower);
158 std::transform(value.begin(), value.end(), value.begin(),
159 (int(*)(int)) tolower);
160 _fields[name] = value;
161 if (name == "keep-alive") {
162 _keepalive = true;
163 if ((value != "on") && (value != "off")) {
164 _max_requests = strtol(value.c_str(), nullptr, 0);
165 // log_debug("Setting Max Requests for Keep-Alive to %d", _max_requests);
166 }
167 }
168 if (name == "connection") {
169 if (value.find("keep-alive", 0) != string::npos) {
170 _keepalive = true;
171 }
172 }
173 if (name == "content-length") {
174 _filesize = strtol(value.c_str(), nullptr, 0);
175 log_debug(_("Setting Content Length to %d"), _filesize);
176 }
177 if (name == "content-type") {
178 // This is the type used by flash when sending a AMF data via POST
179 if (value == "application/x-amf") {
180 // log_debug("Got AMF data in the POST request!");
181 _filetype = DiskStream::FILETYPE_AMF;
182 }
183 // This is the type used by wget when sending a file via POST
184 if (value == "application/x-www-form-urlencoded") {
185 // log_debug("Got file data in the POST request");
186 _filetype = DiskStream::FILETYPE_ENCODED;
187 }
188 log_debug(_("Setting Content Type to %d"), _filetype);
189 }
190
191 // cerr << "FIXME: " << (void *)i << " : " << dec << end << endl;
192 } else {
193 const std::uint8_t *cmd = reinterpret_cast<const std::uint8_t *>(i->c_str());
194 if (extractCommand(const_cast<std::uint8_t *>(cmd)) == HTTP::HTTP_NONE) {
195 break;
196 #if 1
197 } else {
198 log_debug(_("Got a request, parsing \"%s\""), *i);
199 string::size_type start = i->find(" ");
200 string::size_type params = i->find("?");
201 string::size_type pos = i->find("HTTP/");
202 if (pos != string::npos) {
203 // The version is the last field and is the protocol name
204 // followed by a slash, and the version number. Note that
205 // the version is not a double, even though it has a dot
206 // in it. It's actually two separate integers.
207 _version.major = i->at(pos+5) - '0';
208 _version.minor = i->at(pos+7) - '0';
209 // log_debug (_("Version: %d.%d"), _version.major, _version.minor);
210 // the filespec in the request is the middle field, deliminated
211 // by a space on each end.
212 if (params != string::npos) {
213 _params = i->substr(params+1, end);
214 _filespec = i->substr(start+1, params);
215 log_debug(_("Parameters for file: \"%s\""), _params);
216 } else {
217 _filespec = i->substr(start+1, pos-start-2);
218 }
219 log_debug(_("Requesting file: \"%s\""), _filespec);
220
221 // HTTP 1.1 enables persistent network connections
222 // by default.
223 if (_version.minor > 0) {
224 log_debug(_("Enabling Keep Alive by default for HTTP > 1.0"));
225 _keepalive = true;
226 }
227 }
228 }
229 #endif
230 }
231 }
232
233 return buf->reference() + end + 4;
234 }
235
236 // // Parse an Echo Request message coming from the Red5 echo_test. This
237 // // method should only be used for testing purposes.
238 // vector<std::shared_ptr<cygnal::Element > >
239 // HTTP::parseEchoRequest(std::uint8_t *data, size_t size)
240 // {
241 // // GNASH_REPORT_FUNCTION;
242
243 // vector<std::shared_ptr<cygnal::Element > > headers;
244
245 // // skip past the header bytes, we don't care about them.
246 // std::uint8_t *tmpptr = data + 6;
247
248 // std::uint16_t length;
249 // length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
250 // tmpptr += sizeof(std::uint16_t);
251
252 // // Get the first name, which is a raw string, and not preceded by
253 // // a type byte.
254 // std::shared_ptr<cygnal::Element > el1(new cygnal::Element);
255
256 // // If the length of the name field is corrupted, then we get out of
257 // // range quick, and corrupt memory. This is a bit of a hack, but
258 // // reduces memory errors caused by some of the corrupted tes cases.
259 // std::uint8_t *endstr = std::find(tmpptr, tmpptr+length, '\0');
260 // if (endstr != tmpptr+length) {
261 // log_debug("Caught corrupted string! length was %d, null at %d",
262 // length, endstr-tmpptr);
263 // length = endstr-tmpptr;
264 // }
265 // el1->setName(tmpptr, length);
266 // tmpptr += length;
267 // headers.push_back(el1);
268
269 // // Get the second name, which is a raw string, and not preceded by
270 // // a type byte.
271 // length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
272 // tmpptr += sizeof(std::uint16_t);
273 // std::shared_ptr<cygnal::Element > el2(new cygnal::Element);
274
275 // // std::string name2(reinterpret_cast<const char *>(tmpptr), length);
276 // // el2->setName(name2.c_str(), name2.size());
277 // // If the length of the name field is corrupted, then we get out of
278 // // range quick, and corrupt memory. This is a bit of a hack, but
279 // // reduces memory errors caused by some of the corrupted tes cases.
280 // endstr = std::find(tmpptr, tmpptr+length, '\0');
281 // if (endstr != tmpptr+length) {
282 // log_debug("Caught corrupted string! length was %d, null at %d",
283 // length, endstr-tmpptr);
284 // length = endstr-tmpptr;
285 // }
286 // el2->setName(tmpptr, length);
287 // headers.push_back(el2);
288 // tmpptr += length;
289
290 // // Get the last two pieces of data, which are both AMF encoded
291 // // with a type byte.
292 // amf::AMF amf;
293 // std::shared_ptr<cygnal::Element> el3 = amf.extractAMF(tmpptr, tmpptr + size);
294 // headers.push_back(el3);
295 // tmpptr += amf.totalsize();
296
297 // std::shared_ptr<cygnal::Element> el4 = amf.extractAMF(tmpptr, tmpptr + size);
298 // headers.push_back(el4);
299
300 // return headers;
301 // }
302
303 // // format a response to the 'echo' test used for testing Gnash. This
304 // // is only used for testing by developers. The format appears to be
305 // // two strings, followed by a double, followed by the "onResult".
306 // cygnal::Buffer &
307 // HTTP::formatEchoResponse(const std::string &num, cygnal::Element &el)
308 // {
309 // // GNASH_REPORT_FUNCTION;
310 // std::shared_ptr<cygnal::Buffer> data;
311
312 // cygnal::Element nel;
313 // if (el.getType() == cygnal::Element::TYPED_OBJECT_AMF0) {
314 // nel.makeTypedObject();
315 // string name = el.getName();
316 // nel.setName(name);
317 // if (el.propertySize()) {
318 // // FIXME: see about using std::reverse() instead.
319 // for (int i=el.propertySize()-1; i>=0; i--) {
320 // // for (int i=0 ; i<el.propertySize(); i++) {
321 // std::shared_ptr<cygnal::Element> child = el.getProperty(i);
322 // nel.addProperty(child);
323 // }
324 // data = nel.encode();
325 // } else {
326 // data = el.encode();
327 // }
328 // } else {
329 // data = el.encode();
330 // }
331
332 // return formatEchoResponse(num, data->reference(), data->allocated());
333 // }
334
335 #if 0 // FIXME:
336 // Client side parsing of response message codes
337 std::shared_ptr<HTTP::http_response_t>
338 HTTP::parseStatus(const std::string &line)
339 {
340 // GNASH_REPORT_FUNCTION;
341
342 std::shared_ptr<http_response_t> status;
343 // The respnse is a number followed by the error message.
344 string::size_type pos = line.find(" ", 0);
345 if (pos != string::npos) {
346 status.reset(new http_response_t);
347 status->code = static_cast<HTTP::http_status_e>(strtol(line.substr(0, pos).c_str(), NULL, 0));
348 status->msg = line.substr(pos+1, line.size());
349 }
350
351 return status;
352 }
353
354 HTTP::http_method_e
355 HTTP::processClientRequest(int fd)
356 {
357 // GNASH_REPORT_FUNCTION;
358 bool result = false;
359
360 std::shared_ptr<cygnal::Buffer> buf(_que.peek());
361 if (buf) {
362 _cmd = extractCommand(buf->reference());
363 switch (_cmd) {
364 case HTTP::HTTP_GET:
365 result = processGetRequest(fd);
366 break;
367 case HTTP::HTTP_POST:
368 result = processPostRequest(fd);
369 break;
370 case HTTP::HTTP_HEAD:
371 result = processHeadRequest(fd);
372 break;
373 case HTTP::HTTP_CONNECT:
374 result = processConnectRequest(fd);
375 break;
376 case HTTP::HTTP_TRACE:
377 result = processTraceRequest(fd);
378 break;
379 case HTTP::HTTP_OPTIONS:
380 result = processOptionsRequest(fd);
381 break;
382 case HTTP::HTTP_PUT:
383 result = processPutRequest(fd);
384 break;
385 case HTTP::HTTP_DELETE:
386 result = processDeleteRequest(fd);
387 break;
388 default:
389 break;
390 }
391 }
392
393 return _cmd;
394 }
395
396 // A GET request asks the server to send a file to the client
397 bool
398 HTTP::processGetRequest(int fd)
399 {
400 GNASH_REPORT_FUNCTION;
401
402 // std::uint8_t buffer[readsize+1];
403 // const char *ptr = reinterpret_cast<const char *>(buffer);
404 // memset(buffer, 0, readsize+1);
405
406 // _handler->wait();
407 // _handler->dump();
408
409 cerr << "QUE = " << _que.size() << endl;
410
411 if (_que.size() == 0) {
412 return false;
413 }
414
415 std::shared_ptr<cygnal::Buffer> buf(_que.pop());
416 // cerr << "YYYYYYY: " << (char *)buf->reference() << endl;
417 // cerr << hexify(buf->reference(), buf->allocated(), false) << endl;
418
419 if (buf == 0) {
420 // log_debug("Que empty, net connection dropped for fd #%d", getFileFd());
421 log_debug(_("Que empty, net connection dropped for fd #%d"), fd);
422 return false;
423 }
424
425 clearHeader();
426 processHeaderFields(*buf);
427
428 string url = _docroot + _filespec;
429 // See if the file is in the cache and already opened.
430 std::shared_ptr<DiskStream> filestream(cache.findFile(url));
431 if (filestream) {
432 log_network(_("FIXME: found file in cache!"));
433 } else {
434 filestream.reset(new DiskStream);
435 // cerr << "New Filestream at 0x" << hex << filestream.get() << endl;
436
437 // cache.addFile(url, filestream); FIXME: always reload from disk for now.
438
439 // Oopen the file and read the first chunk into memory
440 if (filestream->open(url)) {
441 formatErrorResponse(HTTP::NOT_FOUND);
442 } else {
443 // Get the file size for the HTTP header
444 if (filestream->getFileType() == DiskStream::FILETYPE_NONE) {
445 formatErrorResponse(HTTP::NOT_FOUND);
446 } else {
447 cache.addPath(_filespec, filestream->getFilespec());
448 }
449 }
450 }
451
452 // Send the reply
453 cygnal::Buffer &reply = formatHeader(filestream->getFileType(),
454 filestream->getFileSize(),
455 HTTP::OK);
456 writeNet(fd, reply);
457
458 size_t filesize = filestream->getFileSize();
459 size_t bytes_read = 0;
460 int ret;
461 size_t page = 0;
462 if (filesize) {
463 #ifdef USE_STATS_CACHE
464 struct timespec start;
465 clock_gettime (CLOCK_REALTIME, &start);
466 #endif
467 size_t getbytes = 0;
468 if (filesize <= filestream->getPagesize()) {
469 getbytes = filesize;
470 } else {
471 getbytes = filestream->getPagesize();
472 }
473 if (filesize >= CACHE_LIMIT) {
474 do {
475 filestream->loadToMem(page);
476 ret = writeNet(fd, filestream->get(), getbytes);
477 if (ret <= 0) {
478 break;
479 }
480 bytes_read += ret;
481 page += filestream->getPagesize();
482 } while (bytes_read <= filesize);
483 } else {
484 filestream->loadToMem(filesize, 0);
485 ret = writeNet(fd, filestream->get(), filesize);
486 }
487 filestream->close();
488 #ifdef USE_STATS_CACHE
489 struct timespec end;
490 clock_gettime (CLOCK_REALTIME, &end);
491 double time = (end.tv_sec - start.tv_sec) + ((end.tv_nsec - start.tv_nsec)/1e9);
492 cerr << "File " << _filespec
493 << " transferred " << filesize << " bytes in: " << fixed
494 << time << " seconds for net fd #" << fd << endl;
495 #endif
496 }
497
498 log_debug(_("http_handler all done transferring requested file \"%s\"."),
499 _filespec);
500
501 return true;
502 }
503
504 // A POST request asks sends a data from the client to the server. After processing
505 // the header like we normally do, we then read the amount of bytes specified by
506 // the "content-length" field, and then write that data to disk, or decode the amf.
507 bool
508 HTTP::processPostRequest(int fd)
509 {
510 GNASH_REPORT_FUNCTION;
511
512 // cerr << "QUE1 = " << _que.size() << endl;
513
514 if (_que.size() == 0) {
515 return false;
516 }
517
518 std::shared_ptr<cygnal::Buffer> buf(_que.pop());
519 if (buf == 0) {
520 log_debug(_("Que empty, net connection dropped for fd #%d"), getFileFd());
521 return false;
522 }
523 // cerr << __FUNCTION__ << buf->allocated() << " : " << hexify(buf->reference(), buf->allocated(), true) << endl;
524
525 clearHeader();
526 std::uint8_t *data = processHeaderFields(*buf);
527 size_t length = strtol(getField("content-length").c_str(), NULL, 0);
528 std::shared_ptr<cygnal::Buffer> content(new cygnal::Buffer(length));
529 int ret = 0;
530 if (buf->allocated() - (data - buf->reference()) ) {
531 // cerr << "Don't need to read more data: have " << buf->allocated() << " bytes" << endl;
532 content->copy(data, length);
533 ret = length;
534 } else {
535 // cerr << "Need to read more data, only have " << buf->allocated() << " bytes" << endl;
536 ret = readNet(fd, *content, 2);
537 data = content->reference();
538 }
539
540 if (getField("content-type") == "application/x-www-form-urlencoded") {
541 log_debug(_("Got file data in POST"));
542 string url = _docroot + _filespec;
543 DiskStream ds(url, *content);
544 ds.writeToDisk();
545 // ds.close();
546 // oh boy, we got ourselves some encoded AMF objects instead of a boring file.
547 } else if (getField("content-type") == "application/x-amf") {
548 log_debug(_("Got AMF data in POST"));
549 #if 0
550 amf::AMF amf;
551 std::shared_ptr<cygnal::Element> el = amf.extractAMF(content.reference(), content.end());
552 el->dump(); // FIXME: do something intelligent
553 // with this Element
554 #endif
555 }
556
557 // Send the reply
558
559 // NOTE: this is a "special" path we trap until we have real CGI support
560 if ((_filespec == "/echo/gateway")
561 && (getField("content-type") == "application/x-amf")) {
562 // const char *num = (const char *)buf->at(10);
563 log_debug(_("Got CGI echo request in POST"));
564 // cerr << "FIXME 2: " << hexify(content->reference(), content->allocated(), true) << endl;
565
566 vector<std::shared_ptr<cygnal::Element> > headers = parseEchoRequest(*content);
567 //std::shared_ptr<cygnal::Element> &el0 = headers[0];
568 //std::shared_ptr<cygnal::Element> &el1 = headers[1];
569 //std::shared_ptr<cygnal::Element> &el3 = headers[3];
570
571 if (headers.size() >= 4) {
572 if (headers[3]) {
573 cygnal::Buffer &reply = formatEchoResponse(headers[1]->getName(), *headers[3]);
574 // cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), true) << endl;
575 // cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), false) << endl;
576 writeNet(fd, reply);
577 }
578 }
579 } else {
580 cygnal::Buffer &reply = formatHeader(_filetype, _filesize, HTTP::OK);
581 writeNet(fd, reply);
582 }
583
584 return true;
585 }
586
587 bool
588 HTTP::processPutRequest(int /* fd */)
589 {
590 // GNASH_REPORT_FUNCTION;
591 log_unimpl(_("PUT request"));
592
593 return false;
594 }
595
596 bool
597 HTTP::processDeleteRequest(int /* fd */)
598 {
599 // GNASH_REPORT_FUNCTION;
600 log_unimpl(_("DELETE request"));
601 return false;
602 }
603
604 bool
605 HTTP::processConnectRequest(int /* fd */)
606 {
607 // GNASH_REPORT_FUNCTION;
608 log_unimpl(_("CONNECT request"));
609 return false;
610 }
611
612 bool
613 HTTP::processOptionsRequest(int /* fd */)
614 {
615 // GNASH_REPORT_FUNCTION;
616 log_unimpl(_("OPTIONS request"));
617 return false;
618 }
619
620 bool
621 HTTP::processHeadRequest(int /* fd */)
622 {
623 // GNASH_REPORT_FUNCTION;
624 log_unimpl(_("HEAD request"));
625 return false;
626 }
627
628 bool
629 HTTP::processTraceRequest(int /* fd */)
630 {
631 // GNASH_REPORT_FUNCTION;
632 log_unimpl(_("TRACE request"));
633 return false;
634 }
635 #endif
636
637 // Convert the Content-Length field to a number we can use
638 size_t
getContentLength()639 HTTP::getContentLength()
640 {
641 // GNASH_REPORT_FUNCTION;
642 std::string length = getField("content-length");
643 if (length.size() > 0) {
644 return static_cast<size_t>(strtol(length.c_str(), nullptr, 0));
645 }
646
647 return 0;
648 }
649
650
651 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5 (5.3 Request Header Fields)
652 bool
checkRequestFields(cygnal::Buffer &)653 HTTP::checkRequestFields(cygnal::Buffer & /* buf */)
654 {
655 // GNASH_REPORT_FUNCTION;
656
657 #if 0
658 const char *foo[] = {
659 "Accept",
660 "Accept-Charset",
661 "Accept-Encoding",
662 "Accept-Language",
663 "Authorization",
664 "Expect",
665 "From",
666 "Host",
667 "If-Match",
668 "If-Modified-Since",
669 "If-None-Match",
670 "If-Range",
671 "If-Unmodified-Since",
672 "Max-Forwards",
673 "Proxy-Authorization",
674 "Range",
675 "Referer",
676 "TE",
677 "User-Agent",
678 0
679 };
680 #endif
681 return false;
682 }
683
684 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec7 (7.1 Entity Header Fields)
685 bool
checkEntityFields(cygnal::Buffer &)686 HTTP::checkEntityFields(cygnal::Buffer & /* buf */)
687 {
688 // GNASH_REPORT_FUNCTION;
689
690 #if 0
691 const char *foo[] = {
692 "Accept",
693 "Allow",
694 "Content-Encoding",
695 "Content-Language",
696 "Content-Length", // Must be used when sending a Response
697 "Content-Location",
698 "Content-MD5",
699 "Content-Range",
700 "Content-Type", // Must be used when sending non text/html files
701 "Expires",
702 "Last-Modified",
703 0
704 };
705
706 return true;
707 #endif
708 return false;
709 }
710
711 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec4 (4.5 General Header Fields)
712 bool
checkGeneralFields(cygnal::Buffer &)713 HTTP::checkGeneralFields(cygnal::Buffer & /* buf */)
714 {
715 // GNASH_REPORT_FUNCTION;
716
717 #if 0
718 const char *foo[] = {
719 "Cache-Control"
720 "Connection", // Must look for Keep-Alive and close
721 "Date",
722 "Pragma",
723 "Trailer",
724 "Transfer-Encoding", // Must look for Chunked-Body too
725 "Upgrade",
726 "Via",
727 "Warning",
728 0
729 };
730 #endif
731 return false;
732 }
733
734 std::shared_ptr<std::vector<std::string> >
getFieldItem(const std::string & name)735 HTTP::getFieldItem(const std::string &name)
736 {
737 // GNASH_REPORT_FUNCTION;
738 std::shared_ptr<std::vector<std::string> > ptr(new std::vector<std::string>);
739 Tok t(_fields[name], Sep(", "));
740 for (Tok::iterator i = t.begin(), e = t.end(); i != e; ++i) {
741 ptr->push_back(*i);
742 }
743
744 return ptr;
745 }
746
747 bool
startHeader()748 HTTP::startHeader()
749 {
750 // GNASH_REPORT_FUNCTION;
751
752 clearHeader();
753
754 return true;
755 }
756
757 cygnal::Buffer &
formatHeader(http_status_e type)758 HTTP::formatHeader(http_status_e type)
759 {
760 // GNASH_REPORT_FUNCTION;
761
762 return formatHeader(_filesize, type);
763 }
764
765 cygnal::Buffer &
formatCommon(const string & data)766 HTTP::formatCommon(const string &data)
767 {
768 // GNASH_REPORT_FUNCTION;
769 _buffer += data;
770 _buffer += "\r\n";
771
772 return _buffer;
773 }
774
775 cygnal::Buffer &
formatHeader(size_t size,http_status_e code)776 HTTP::formatHeader(size_t size, http_status_e code)
777 {
778 // GNASH_REPORT_FUNCTION;
779 return formatHeader(_filetype, size, code);
780 }
781
782 cygnal::Buffer &
formatHeader(DiskStream::filetype_e type,size_t size,http_status_e code)783 HTTP::formatHeader(DiskStream::filetype_e type, size_t size, http_status_e code)
784 {
785 // GNASH_REPORT_FUNCTION;
786
787 clearHeader();
788
789 char num[12];
790
791 _buffer = "HTTP/";
792 sprintf(num, "%d.%d", _version.major, _version.minor);
793 _buffer += num;
794 sprintf(num, " %d ", static_cast<int>(code));
795 _buffer += num;
796 switch (code) {
797 case CONTINUE:
798 _buffer += "Continue";
799 break;
800 case SWITCHPROTOCOLS:
801 _buffer += "Switch Protocols";
802 break;
803 // 2xx: Success - The action was successfully received,
804 // understood, and accepted
805 break;
806 case OK:
807 _buffer += "OK";
808 break;
809 case CREATED:
810 _buffer += "Created";
811 break;
812 case ACCEPTED:
813 _buffer += "Accepted";
814 break;
815 case NON_AUTHORITATIVE:
816 _buffer += "Non Authoritive";
817 break;
818 case NO_CONTENT:
819 _buffer += "No Content";
820 break;
821 case RESET_CONTENT:
822 _buffer += "Reset Content";
823 break;
824 case PARTIAL_CONTENT:
825 _buffer += "Partial Content";
826 break;
827 // 3xx: Redirection - Further action must be taken in order to
828 // complete the request
829 case MULTIPLE_CHOICES:
830 _buffer += "Multiple Choices";
831 break;
832 case MOVED_PERMANENTLY:
833 _buffer += "Moved Permanently";
834 break;
835 case FOUND:
836 _buffer += "Found";
837 break;
838 case SEE_OTHER:
839 _buffer += "See Other";
840 break;
841 case NOT_MODIFIED:
842 _buffer += "Not Modified";
843 break;
844 case USE_PROXY:
845 _buffer += "Use Proxy";
846 break;
847 case TEMPORARY_REDIRECT:
848 _buffer += "Temporary Redirect";
849 break;
850 // 4xx: Client Error - The request contains bad syntax or
851 // cannot be fulfilled
852 case BAD_REQUEST:
853 _buffer += "Bad Request";
854 break;
855 case UNAUTHORIZED:
856 _buffer += "Unauthorized";
857 break;
858 case PAYMENT_REQUIRED:
859 _buffer += "Payment Required";
860 break;
861 case FORBIDDEN:
862 _buffer += "Forbidden";
863 break;
864 case NOT_FOUND:
865 _buffer += "Not Found";
866 break;
867 case METHOD_NOT_ALLOWED:
868 _buffer += "Method Not Allowed";
869 break;
870 case NOT_ACCEPTABLE:
871 _buffer += "Not Acceptable";
872 break;
873 case PROXY_AUTHENTICATION_REQUIRED:
874 _buffer += "Proxy Authentication Required";
875 break;
876 case REQUEST_TIMEOUT:
877 _buffer += "Request Timeout";
878 break;
879 case CONFLICT:
880 _buffer += "Conflict";
881 break;
882 case GONE:
883 _buffer += "Gone";
884 break;
885 case LENGTH_REQUIRED:
886 _buffer += "Length Required";
887 break;
888 case PRECONDITION_FAILED:
889 _buffer += "Precondition Failed";
890 break;
891 case REQUEST_ENTITY_TOO_LARGE:
892 _buffer += "Request Entity Too Large";
893 break;
894 case REQUEST_URI_TOO_LARGE:
895 _buffer += "Request URI Too Large";
896 break;
897 case UNSUPPORTED_MEDIA_TYPE:
898 _buffer += "Unsupported Media Type";
899 break;
900 case REQUESTED_RANGE_NOT_SATISFIABLE:
901 _buffer += "Request Range Not Satisfiable";
902 break;
903 case EXPECTATION_FAILED:
904 _buffer += "Expectation Failed";
905 break;
906 // 5xx: Server Error - The server failed to fulfill an apparently valid request
907 case INTERNAL_SERVER_ERROR:
908 _buffer += "Internal Server Error";
909 break;
910 case NOT_IMPLEMENTED:
911 _buffer += "Method Not Implemented";
912 break;
913 case BAD_GATEWAY:
914 _buffer += "Bad Gateway";
915 break;
916 case SERVICE_UNAVAILABLE:
917 _buffer += "Service Unavailable";
918 break;
919 case GATEWAY_TIMEOUT:
920 _buffer += "Gateway Timeout";
921 break;
922 case HTTP_VERSION_NOT_SUPPORTED:
923 _buffer += "HTTP Version Not Supported";
924 break;
925 // Gnash/Cygnal extensions for internal use
926 case LIFE_IS_GOOD:
927 break;
928 case CLOSEPIPE:
929 _buffer += "Close Pipe";
930 break;
931 default:
932 break;
933 }
934
935 // end the line
936 _buffer += "\r\n";
937
938 formatDate();
939 formatServer();
940 formatLastModified();
941 formatAcceptRanges("bytes");
942 formatContentLength(size);
943
944 // Apache closes the connection on GET requests, so we do the same.
945 // This is a bit silly, because if we close after every GET request,
946 // we're not really handling the persistance of HTTP 1.1 at all.
947 if (_close) {
948 formatConnection("close");
949 _keepalive = false;
950 }
951 formatContentType(type);
952
953 // All HTTP messages are followed by a blank line.
954 terminateHeader();
955
956 return _buffer;
957 }
958
959 cygnal::Buffer &
formatDate()960 HTTP::formatDate()
961 {
962 // GNASH_REPORT_FUNCTION;
963 boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
964
965 // cout << now.time_of_day() << "\r\n";
966
967 boost::gregorian::date d(now.date());
968
969 char num[12];
970
971 boost::gregorian::greg_weekday wd = d.day_of_week();
972 _buffer += "Date: ";
973 _buffer += wd.as_long_string();
974
975 _buffer += ", ";
976 sprintf(num, "%d", static_cast<int>(d.day()));
977 _buffer += num;
978
979 _buffer += " ";
980 _buffer += boost::gregorian::greg_month(d.month()).as_short_string();
981
982 _buffer += " ";
983 sprintf(num, "%d", static_cast<int>(d.year()));
984 _buffer += num;
985
986 _buffer += " ";
987 _buffer += boost::posix_time::to_simple_string(now.time_of_day());
988
989 _buffer += " GMT\r\n";
990
991 return _buffer;
992 }
993
994 cygnal::Buffer &
formatServer()995 HTTP::formatServer()
996 {
997 // GNASH_REPORT_FUNCTION;
998 _buffer += "Server: Cygnal (GNU/Linux)\r\n";
999
1000 return _buffer;
1001 }
1002
1003 cygnal::Buffer &
formatServer(const string & data)1004 HTTP::formatServer(const string &data)
1005 {
1006 // GNASH_REPORT_FUNCTION;
1007 _buffer += "Server: ";
1008 _buffer += data;
1009 _buffer += "\r\n";
1010
1011 return _buffer;
1012 }
1013
1014 cygnal::Buffer &
formatContentLength()1015 HTTP::formatContentLength()
1016 {
1017 // GNASH_REPORT_FUNCTION;
1018
1019 return formatContentLength(_filesize);
1020 }
1021
1022 cygnal::Buffer &
formatContentLength(std::uint32_t filesize)1023 HTTP::formatContentLength(std::uint32_t filesize)
1024 {
1025 // GNASH_REPORT_FUNCTION;
1026 // _header << "Content-Length: " << filesize << "\r\n";
1027
1028 _buffer += "Content-Length: ";
1029 char num[12];
1030 sprintf(num, "%d", filesize);
1031 _buffer += num;
1032 _buffer += "\r\n";
1033
1034 return _buffer;
1035 }
1036
1037 cygnal::Buffer &
formatContentType()1038 HTTP::formatContentType()
1039 {
1040 // GNASH_REPORT_FUNCTION;
1041 return formatContentType(_filetype);
1042 }
1043
1044 cygnal::Buffer &
formatContentType(DiskStream::filetype_e filetype)1045 HTTP::formatContentType(DiskStream::filetype_e filetype)
1046 {
1047 // GNASH_REPORT_FUNCTION;
1048
1049 switch (filetype) {
1050 // default to HTML if the type isn't known
1051 case DiskStream::FILETYPE_NONE:
1052 _buffer += "Content-Type: text/html\r\n";
1053 break;
1054 case DiskStream::FILETYPE_AMF:
1055 _buffer += "Content-Type: application/x-amf\r\n";
1056 break;
1057 case DiskStream::FILETYPE_SWF:
1058 _buffer += "Content-Type: application/x-shockwave-flash\r\n";
1059 break;
1060 case DiskStream::FILETYPE_HTML:
1061 _buffer += "Content-Type: text/html\r\n";
1062 break;
1063 case DiskStream::FILETYPE_PNG:
1064 _buffer += "Content-Type: image/png\r\n";
1065 break;
1066 case DiskStream::FILETYPE_JPEG:
1067 _buffer += "Content-Type: image/jpeg\r\n";
1068 break;
1069 case DiskStream::FILETYPE_GIF:
1070 _buffer += "Content-Type: image/gif\r\n";
1071 break;
1072 case DiskStream::FILETYPE_MP3:
1073 _buffer += "Content-Type: audio/mpeg\r\n";
1074 break;
1075 case DiskStream::FILETYPE_MP4:
1076 _buffer += "Content-Type: video/mp4\r\n";
1077 break;
1078 case DiskStream::FILETYPE_OGG:
1079 _buffer += "Content-Type: audio/ogg\r\n";
1080 break;
1081 case DiskStream::FILETYPE_VORBIS:
1082 _buffer += "Content-Type: audio/ogg\r\n";
1083 break;
1084 case DiskStream::FILETYPE_THEORA:
1085 _buffer += "Content-Type: video/ogg\r\n";
1086 break;
1087 case DiskStream::FILETYPE_DIRAC:
1088 _buffer += "Content-Type: video/dirac\r\n";
1089 break;
1090 case DiskStream::FILETYPE_TEXT:
1091 _buffer += "Content-Type: text/plain\r\n";
1092 break;
1093 case DiskStream::FILETYPE_FLV:
1094 _buffer += "Content-Type: video/x-flv\r\n";
1095 break;
1096 case DiskStream::FILETYPE_VP6:
1097 _buffer += "Content-Type: video/vp6\r\n";
1098 break;
1099 case DiskStream::FILETYPE_XML:
1100 _buffer += "Content-Type: application/xml\r\n";
1101 break;
1102 case DiskStream::FILETYPE_FLAC:
1103 _buffer += "Content-Type: audio/flac\r\n";
1104 break;
1105 case DiskStream::FILETYPE_PHP:
1106 _buffer += "Content-Type: application/x-httpd-php\r\n";
1107 break;
1108 default:
1109 _buffer += "Content-Type: text/html\r\n";
1110 }
1111
1112 return _buffer;
1113 }
1114
1115 cygnal::Buffer &
formatLastModified()1116 HTTP::formatLastModified()
1117 {
1118 // GNASH_REPORT_FUNCTION;
1119 boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
1120 std::stringstream date;
1121
1122 boost::gregorian::date d(now.date());
1123
1124 date << d.day_of_week();
1125 date << ", " << d.day();
1126 date << " " << d.month();
1127 date << " " << d.year();
1128 date << " " << now.time_of_day();
1129 date << " GMT";
1130
1131 return formatLastModified(date.str());
1132 }
1133
1134 cygnal::Buffer &
formatEchoResponse(const std::string & num,cygnal::Buffer & data)1135 HTTP::formatEchoResponse(const std::string &num, cygnal::Buffer &data)
1136 {
1137 // GNASH_REPORT_FUNCTION;
1138 return formatEchoResponse(num, data.reference(), data.allocated());
1139 }
1140
1141 cygnal::Buffer &
formatEchoResponse(const std::string & num,std::uint8_t * data,size_t size)1142 HTTP::formatEchoResponse(const std::string &num, std::uint8_t *data, size_t size)
1143 {
1144 // GNASH_REPORT_FUNCTION;
1145
1146 //std::uint8_t *tmpptr = data;
1147
1148 // FIXME: temporary hacks while debugging
1149 cygnal::Buffer fixme("00 00 00 00 00 01");
1150 cygnal::Buffer fixme2("ff ff ff ff");
1151
1152 _buffer = "HTTP/1.1 200 OK\r\n";
1153 formatContentType(DiskStream::FILETYPE_AMF);
1154 // formatContentLength(size);
1155 // FIXME: this is a hack ! Calculate a real size!
1156 formatContentLength(size+29);
1157
1158 // Don't pretend to be the Red5 server
1159 formatServer("Cygnal (0.8.6)");
1160
1161 // All HTTP messages are followed by a blank line.
1162 terminateHeader();
1163
1164 // Add the binary blob for the header
1165 _buffer += fixme;
1166
1167 // Make the result response, which is the 2nd data item passed in
1168 // the request, a slash followed by a number like "/2".
1169 string result = num;
1170 result += "/onResult";
1171 std::shared_ptr<cygnal::Buffer> res = cygnal::AMF::encodeString(result);
1172 _buffer.append(res->begin()+1, res->size()-1);
1173
1174 // Add the null data item
1175 std::shared_ptr<cygnal::Buffer> null = cygnal::AMF::encodeString("null");
1176 _buffer.append(null->begin()+1, null->size()-1);
1177
1178 // Add the other binary blob
1179 _buffer += fixme2;
1180
1181 cygnal::Element::amf0_type_e type = static_cast<cygnal::Element::amf0_type_e>(*data);
1182 if ((type == cygnal::Element::UNSUPPORTED_AMF0)
1183 || (type == cygnal::Element::NULL_AMF0)) {
1184 _buffer += type;
1185 // Red5 returns a NULL object when it's recieved an undefined one in the echo_test
1186 } else if (type == cygnal::Element::UNDEFINED_AMF0) {
1187 _buffer += cygnal::Element::NULL_AMF0;
1188 } else {
1189 // Add the AMF data we're echoing back
1190 if (size) {
1191 _buffer.append(data, size);
1192 }
1193 }
1194
1195 return _buffer;
1196 }
1197
1198 cygnal::Buffer &
formatRequest(const string & url,http_method_e cmd)1199 HTTP::formatRequest(const string &url, http_method_e cmd)
1200 {
1201 // GNASH_REPORT_FUNCTION;
1202
1203 clearHeader();
1204
1205 switch (cmd) {
1206 case HTTP::HTTP_GET:
1207 _buffer = "GET ";
1208 break;
1209 case HTTP::HTTP_POST:
1210 _buffer = "POST ";
1211 break;
1212 case HTTP::HTTP_HEAD:
1213 _buffer = "HEAD ";
1214 break;
1215 case HTTP::HTTP_CONNECT:
1216 _buffer = "CONNECT ";
1217 break;
1218 case HTTP::HTTP_TRACE:
1219 _buffer = "TRACE ";
1220 break;
1221 case HTTP::HTTP_OPTIONS:
1222 _buffer = "OPTIONS ";
1223 break;
1224 default:
1225 break;
1226 }
1227 _buffer += url;
1228
1229 _buffer += " HTTP/1.1";
1230 // sprintf(num, "%d.%d", _version.major, _version.minor);
1231 // _buffer += num;
1232 // end the line
1233 _buffer += "\r\n";
1234
1235 formatHost("localhost");
1236 formatAgent("Gnash");
1237
1238 // Post messages want a bunch more fields than a GET request
1239 if (cmd == HTTP::HTTP_POST) {
1240 formatContentType(DiskStream::FILETYPE_AMF);
1241 formatEncoding("deflate, gzip, x-gzip, identity, *;q=0");
1242 formatConnection("Keep-Alive");
1243 }
1244
1245 // // All HTTP messages are followed by a blank line.
1246 // terminateHeader();
1247
1248 return _buffer;
1249 }
1250
1251
1252 HTTP::http_method_e
extractCommand(std::uint8_t * data)1253 HTTP::extractCommand(std::uint8_t *data)
1254 {
1255 // GNASH_REPORT_FUNCTION;
1256
1257 // string body = reinterpret_cast<const char *>(data);
1258 HTTP::http_method_e cmd = HTTP::HTTP_NONE;
1259
1260 // force the case to make comparisons easier
1261 // std::transform(body.begin(), body.end(), body.begin(),
1262 // (int(*)(int)) toupper);
1263
1264 // Extract the command
1265 if (memcmp(data, "GET", 3) == 0) {
1266 cmd = HTTP::HTTP_GET;
1267 } else if (memcmp(data, "POST", 4) == 0) {
1268 cmd = HTTP::HTTP_POST;
1269 } else if (memcmp(data, "HEAD", 4) == 0) {
1270 cmd = HTTP::HTTP_HEAD;
1271 } else if (memcmp(data, "CONNECT", 7) == 0) {
1272 cmd = HTTP::HTTP_CONNECT;
1273 } else if (memcmp(data, "TRACE", 5) == 0) {
1274 cmd = HTTP::HTTP_TRACE;
1275 } else if (memcmp(data, "PUT", 3) == 0) {
1276 cmd = HTTP::HTTP_PUT;
1277 } else if (memcmp(data, "OPTIONS", 4) == 0) {
1278 cmd = HTTP::HTTP_OPTIONS;
1279 } else if (memcmp(data, "DELETE", 4) == 0) {
1280 cmd = HTTP::HTTP_DELETE;
1281 } else if (memcmp(data, "HTTP", 4) == 0) {
1282 cmd = HTTP::HTTP_RESPONSE;
1283 }
1284
1285 // For valid requests, the second argument, delimited by spaces
1286 // is the filespec of the file being requested or transmitted.
1287 if (cmd != HTTP::HTTP_NONE) {
1288 std::uint8_t *start = std::find(data, data+7, ' ') + 1;
1289 std::uint8_t *end = std::find(start + 2, data+PATH_MAX, ' ');
1290 std::uint8_t *params = std::find(start, end, '?');
1291 if (params != end) {
1292 _params = std::string(params+1, end);
1293 _filespec = std::string(start, params);
1294 log_debug(_("Parameters for file: \"%s\""), _params);
1295 } else {
1296 // This is fine as long as end is within the buffer.
1297 _filespec = std::string(start, end);
1298 }
1299 // log_debug("Requesting file: \"%s\"", _filespec);
1300
1301 // The third field is always the HTTP version
1302 // The version is the last field and is the protocol name
1303 // followed by a slash, and the version number. Note that
1304 // the version is not a double, even though it has a dot
1305 // in it. It's actually two separate integers.
1306 _version.major = *(end+6) - '0';
1307 _version.minor = *(end+8) - '0';
1308 // log_debug (_("Version: %d.%d"), _version.major, _version.minor);
1309 }
1310
1311 return cmd;
1312 }
1313
1314 /// \brief Send a message to the other end of the network connection.
1315 ///` Sends the contents of the _header and _body private data to
1316 /// the already opened network connection.
1317 ///
1318 /// @return The number of bytes sent
1319 int DSOEXPORT
sendMsg()1320 HTTP::sendMsg()
1321 {
1322 GNASH_REPORT_FUNCTION;
1323
1324 return 0; // FIXME
1325 }
1326
1327 /// \brief Send a message to the other end of the network connection.
1328 ///` Sends the contents of the _header and _body private data to
1329 /// the already opened network connection.
1330 ///
1331 /// @param fd The file descriptor to use for writing to the network.
1332 ///
1333 /// @return The number of bytes sent
1334 int DSOEXPORT
sendMsg(int)1335 HTTP::sendMsg(int /* fd */)
1336 {
1337 GNASH_REPORT_FUNCTION;
1338
1339 return 0; // FIXME
1340 }
1341
1342 /// \brief Send a message to the other end of the network connection.
1343 ///` Sends the contents of the _header and _body private data to
1344 /// the already opened network connection.
1345 ///
1346 /// @param data A real pointer to the data.
1347 /// @param size The number of bytes of data stored.
1348 ///
1349 /// @return The number of bytes sent
1350 int DSOEXPORT
sendMsg(const std::uint8_t * data,size_t size)1351 HTTP::sendMsg(const std::uint8_t *data, size_t size)
1352 {
1353 GNASH_REPORT_FUNCTION;
1354 // _header
1355
1356 return Network::writeNet(data, size);
1357 }
1358
1359 size_t
recvChunked(std::uint8_t * data,size_t size)1360 HTTP::recvChunked(std::uint8_t *data, size_t size)
1361 {
1362 // GNASH_REPORT_FUNCTION;
1363 bool done = false;
1364 bool chunks = true;
1365 size_t total = 0;
1366 size_t pktsize = 0;
1367
1368 if (size == 0) {
1369 return 0;
1370 }
1371
1372 // A chunked transfer sends a count of bytes in ASCII hex first,
1373 // and that line is terminated with the usual \r\n HTTP header field
1374 // line number. There is supposed to be a ';' before the \r\n, as this
1375 // field can have other attributes, but the OpenStreetMap server doesn't
1376 // use the semi-colon, as it's optional, and rarely used anyway.
1377 std::shared_ptr<cygnal::Buffer> buf;
1378 std::uint8_t *start = std::find(data, data+size, '\r') + 2;
1379 if (start != data+size) {
1380 // extract the total size of the chunk
1381 std::string bytes(data, start-2);
1382 size_t sizesize = start-data;
1383 total = static_cast<size_t>(strtol(bytes.c_str(), nullptr, 16));
1384 log_debug(_("%s: Total size for first chunk is: %d, data size %d (%d)"),
1385 __PRETTY_FUNCTION__, total, size, sizesize);
1386 buf.reset(new cygnal::Buffer(total+2));
1387 // Add the existing data from the previous packet
1388 buf->copy(data+sizesize, size-sizesize);
1389 }
1390
1391 // The size of the packet we need to read has a 2 byte terminator "\r\n",
1392 // like any other HTTP header field, so we have to read those bytes too
1393 // so we can stay sychronized with the start of each chunk.
1394 pktsize = total - buf->allocated() + 2;
1395 // log_debug("%s: Total Packet size for first chunk is: %d", __PRETTY_FUNCTION__,
1396 // pktsize);
1397
1398 done = false;
1399 size_t ret = 0;
1400
1401 // Keep reading chunks as long as they arrive
1402 while (chunks) {
1403 do {
1404 if (!pktsize) {
1405 total = 0;
1406 // Only read a few bytes, as we have todo a resize, so the less
1407 // data to copy the better. We only do this to get the total size
1408 // of the chunk, which we need to know when it's done. As we can't
1409 // tell we're done processing chunks till one has a length of
1410 // "0\r\n", this is important.
1411 pktsize = 12;
1412 buf.reset(new cygnal::Buffer(pktsize+2));
1413 }
1414 ret = readNet(buf->reference() + buf->allocated(), pktsize, 60);
1415 //buf->dump();
1416 // We got data.
1417 if (ret == 0) {
1418 log_debug(_("no data yet for fd #%d, continuing..."),
1419 getFileFd());
1420 done = true;
1421 }
1422 if (ret) {
1423 // manually set the seek pointer in the buffer, as we read
1424 // the data into the raw memory allocated to the buffer. We
1425 // only want to do this if we got data of course.
1426 buf->setSeekPointer(buf->end() + ret);
1427 if (!total) {
1428 start = std::find(buf->reference(), buf->reference()+ret, '\r') + 2;
1429 if (start != buf->reference()+ret) {
1430 // extract the total size of the chunk
1431 std::string bytes(buf->reference(), start-2);
1432 total = static_cast<size_t>(strtol(bytes.c_str(), nullptr, 16));
1433 // The total size of the last chunk is always "0"
1434 if (total == 0) {
1435 log_debug(_("%s: end of chunks!"), __PRETTY_FUNCTION__);
1436 pktsize = 0;
1437 done = true;
1438 chunks = false;
1439 } else {
1440 pktsize = total+8; // FIXME: why do we need an 8 here ?
1441 // log_debug("%s: Total size for chunk is: %d (%s), adding %d bytes",
1442 // __PRETTY_FUNCTION__, total, bytes, (start - buf->reference()));
1443 cygnal::Buffer tmpbuf(start - buf->reference());
1444 // don't forget the two bytes for the "\r\n"
1445 tmpbuf.copy(buf->reference() + bytes.size() + 2, (start - buf->reference()));
1446 buf->clear(); // FIXME: debug only
1447 buf->resize(total);
1448 buf->copy(tmpbuf.reference(), tmpbuf.size());
1449 }
1450 }
1451 }
1452
1453 // If we got less data than specified, we need to keep reading data
1454 if (ret < buf->size()) {
1455 pktsize -= ret;
1456 if (pktsize == 0) {
1457 done = true;
1458 } else {
1459 // log_debug("Didn't get a full packet, reading more data");
1460 continue;
1461 }
1462 }
1463 }
1464 } while (!done);
1465 // log_debug("%s: finished packet, ret is %d, pktsize is %d\r\n%s", __PRETTY_FUNCTION__, ret, pktsize,
1466 // hexify(buf->reference(), buf->allocated(), true));
1467 if (pktsize == 0) {
1468 // The last two bytes of the chunk are always "\r\n", which we remove so it
1469 // doesn't become part of the binary data being sent in the chunk.
1470 if ((*(buf->end()-2) == '\r') && (*(buf->end()-1) == '\n')) {
1471 *(buf->end()-2) = 0; // '\r'
1472 *(buf->end()-1) = 0; // '\n'
1473 buf->setSeekPointer(buf->end() - 2);
1474 }
1475 _que.push(buf);
1476 }
1477 done = false; // reset
1478 } // end of while chunks
1479
1480 return _que.size();
1481 }
1482
1483 int
recvMsg(int fd)1484 HTTP::recvMsg(int fd)
1485 {
1486 // GNASH_REPORT_FUNCTION;
1487 return recvMsg(fd, 0);
1488 }
1489
1490 int
recvMsg(int fd,size_t size)1491 HTTP::recvMsg(int fd, size_t size)
1492 {
1493 // GNASH_REPORT_FUNCTION;
1494
1495 size_t ret = 0;
1496
1497 if (size == 0) {
1498 size = cygnal::NETBUFSIZE;
1499 }
1500
1501 log_debug(_("Starting to wait for data in net for fd #%d"), fd);
1502 Network net;
1503
1504 do {
1505 std::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(size));
1506 ret = net.readNet(fd, *buf, 5);
1507 // cerr << __PRETTY_FUNCTION__ << ret << " : " << (char *)buf->reference() << endl;
1508
1509 // the read timed out as there was no data, but the socket is still open.
1510 if (ret == 0) {
1511 log_debug(_("no data yet for fd #%d, continuing..."), fd);
1512 continue;
1513 }
1514 // ret is "no position" when the socket is closed from the other end of the connection,
1515 // so we're done.
1516 if ((ret == static_cast<size_t>(string::npos)) || (static_cast<int>(ret) == -1)) {
1517 log_debug(_("socket for fd #%d was closed..."), fd);
1518 return 0;
1519 }
1520 // We got data. Resize the buffer if necessary.
1521 if (ret > 0) {
1522 buf->setSeekPointer(buf->reference() + ret);
1523 // cerr << "XXXXX: " << (char *)buf->reference() << endl;
1524 if (ret < static_cast<int>(cygnal::NETBUFSIZE)) {
1525 // buf->resize(ret); FIXME: why does this corrupt
1526 // the buffer ?
1527 _que.push(buf);
1528 break;
1529 } else {
1530 _que.push(buf);
1531 }
1532 // ret must be more than 0 here
1533 if (ret == buf->size()) {
1534 continue;
1535 }
1536 } else {
1537 log_debug(_("no more data for fd #%d, exiting..."), fd);
1538 return 0;
1539 }
1540 if (static_cast<int>(ret) == -1) {
1541 log_debug(_("Handler done for fd #%d, can't read any data..."), fd);
1542 return -1;
1543 }
1544 } while (ret);
1545
1546 // We're done. Notify the other threads the socket is closed, and tell them to die.
1547 log_debug(_("Done receiving data for fd #%d..."), fd);
1548
1549 return ret;
1550 }
1551
1552
1553 void
dump()1554 HTTP::dump() {
1555 // GNASH_REPORT_FUNCTION;
1556
1557 std::lock_guard<std::mutex> lock(stl_mutex);
1558
1559 log_debug (_("==== The HTTP header breaks down as follows: ===="));
1560 log_debug (_("Filespec: %s"), _filespec.c_str());
1561 log_debug (_("Version: %d.%d"), _version.major, _version.minor);
1562
1563 std::map<string, string>::const_iterator it;
1564 for (it = _fields.begin(); it != _fields.end(); ++it) {
1565 log_debug(_("Field: \"%s\" = \"%s\""), it->first, it->second);
1566 }
1567
1568 // Dump the RTMPT fields
1569 log_debug(_("RTMPT optional index is: "), _index);
1570 log_debug(_("RTMPT optional client ID is: "), _clientid);
1571 log_debug(_("==== ==== ===="));
1572 }
1573
1574 } // end of gnash namespace
1575
1576
1577 // local Variables:
1578 // mode: C++
1579 // indent-tabs-mode: nil
1580 // End:
1581