~ubuntu-branches/ubuntu/precise/gnash/precise-proposed

« back to all changes in this revision

Viewing changes to .pc/fixtypos.patch/cygnal/libnet/http.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Angel Abad, Angel Abad, Micah Gersten
  • Date: 2010-11-28 22:18:48 UTC
  • mfrom: (3.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20101128221848-apjipwy78m13612a
Tags: 0.8.8-6ubuntu1
[ Angel Abad <angelabad@ubuntu.com> ]
* Merge from debian unstable (LP: #682386). Remaining changes:
  - Add Ubuntu flash alternatives in postinst and prerm
    + update debian/browser-plugin-gnash.postinst
    + update debian/browser-plugin-gnash.prerm

[ Micah Gersten <micahg@ubuntu.com> ]
* Only install the flash alternative in /usr/lib/mozilla/plugins as the other
  locations are deprecated
  - update debian/browser-plugin-gnash.postinst

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// http.cpp:  HyperText Transport Protocol handler for Cygnal, for Gnash.
2
 
// 
3
 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4
 
//   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 <boost/thread/mutex.hpp>
22
 
#include <boost/shared_ptr.hpp>
23
 
#include <boost/shared_array.hpp>
24
 
#include <boost/scoped_array.hpp>
25
 
#include <boost/tokenizer.hpp>
26
 
#include <boost/date_time/posix_time/posix_time.hpp>
27
 
#include <boost/date_time/gregorian/gregorian.hpp>
28
 
#include <sys/types.h>
29
 
#include <sys/stat.h>
30
 
#include <fcntl.h>
31
 
#include <string>
32
 
#include <iostream>
33
 
#include <cstring>
34
 
#include <sstream>
35
 
#include <sys/types.h>
36
 
#include <sys/stat.h>
37
 
#include <algorithm>
38
 
 
39
 
#include "GnashSystemIOHeaders.h" // read()
40
 
#include "http.h"
41
 
#include "amf.h"
42
 
#include "element.h"
43
 
#include "cque.h"
44
 
#include "log.h"
45
 
#include "network.h"
46
 
// #include "handler.h"
47
 
#include "utility.h"
48
 
#include "buffer.h"
49
 
#include "diskstream.h"
50
 
#include "cache.h"
51
 
 
52
 
// Not POSIX, so best not rely on it if possible.
53
 
#ifndef PATH_MAX
54
 
# define PATH_MAX 1024
55
 
#endif
56
 
 
57
 
#if defined(_WIN32) || defined(WIN32)
58
 
# define __PRETTY_FUNCTION__ __FUNCDNAME__
59
 
# include <winsock2.h>
60
 
# include <direct.h>
61
 
#else
62
 
# include <unistd.h>
63
 
# include <sys/param.h>
64
 
#endif
65
 
 
66
 
using std::string;
67
 
 
68
 
static boost::mutex stl_mutex;
69
 
 
70
 
namespace gnash
71
 
{
72
 
 
73
 
// extern map<int, Handler *> handlers;
74
 
 
75
 
// FIXME, this seems too small to me.  --gnu
76
 
static const int readsize = 1024;
77
 
 
78
 
static Cache& cache = Cache::getDefaultInstance();
79
 
 
80
 
HTTP::HTTP() 
81
 
    : _filetype(DiskStream::FILETYPE_HTML),
82
 
      _filesize(0),
83
 
      _keepalive(false),
84
 
//       _handler(0),
85
 
      _clientid(0),
86
 
      _index(0),
87
 
      _max_requests(0),
88
 
      _close(false)
89
 
{
90
 
//    GNASH_REPORT_FUNCTION;
91
 
//    struct status_codes *status = new struct status_codes;
92
 
    _version.major = 0;
93
 
    _version.minor = 0;
94
 
    
95
 
//    _status_codes(CONTINUE, status);
96
 
}
97
 
 
98
 
#if 0
99
 
HTTP::HTTP(Handler *hand) 
100
 
    : _filetype(DiskStream::FILETYPE_HTML),
101
 
      _filesize(0),
102
 
      _keepalive(false),
103
 
      _clientid(0),
104
 
      _index(0),
105
 
      _max_requests(0),
106
 
      _close(false)
107
 
 
108
 
{
109
 
//    GNASH_REPORT_FUNCTION;
110
 
//     _handler = hand;
111
 
    _version.major = 0;
112
 
    _version.minor = 0;
113
 
}
114
 
#endif
115
 
 
116
 
HTTP::~HTTP()
117
 
{
118
 
//    GNASH_REPORT_FUNCTION;
119
 
}
120
 
 
121
 
bool
122
 
HTTP::clearHeader()
123
 
{
124
 
//     _header.str("");
125
 
    _buffer.clear();
126
 
    _filesize = 0;
127
 
    _max_requests = 0;
128
 
    
129
 
    return true;
130
 
}
131
 
 
132
 
HTTP &
133
 
HTTP::operator = (HTTP& /*obj*/)
134
 
{
135
 
    GNASH_REPORT_FUNCTION;
136
 
//    this = obj;
137
 
    // TODO: FIXME !
138
 
    return *this; 
139
 
}
140
 
 
141
 
 
142
 
boost::uint8_t *
143
 
HTTP::processHeaderFields(cygnal::Buffer *buf)
144
 
{
145
 
  //    GNASH_REPORT_FUNCTION;
146
 
    string head(reinterpret_cast<const char *>(buf->reference()), buf->size());
147
 
 
148
 
    // The end of the header block is always followed by a blank line
149
 
    string::size_type end = head.find("\r\n\r\n", 0);
150
 
//    head.erase(end, buf.size()-end);
151
 
    Tok t(head, Sep("\r\n"));
152
 
    for (Tok::iterator i = t.begin(); i != t.end(); ++i) {
153
 
        string::size_type pos = i->find(":", 0);
154
 
        if (pos != string::npos) {
155
 
            string name = i->substr(0, pos);
156
 
            string value = i->substr(pos+2, i->size());
157
 
            std::transform(name.begin(), name.end(), name.begin(), 
158
 
                           (int(*)(int)) tolower);
159
 
            std::transform(value.begin(), value.end(), value.begin(), 
160
 
                           (int(*)(int)) tolower);
161
 
            _fields[name] = value;
162
 
            if (name == "keep-alive") {
163
 
                _keepalive = true;
164
 
                if ((value != "on") && (value != "off")) {
165
 
                    _max_requests = strtol(value.c_str(), NULL, 0);
166
 
                    // log_debug("Setting Max Requests for Keep-Alive to %d", _max_requests);
167
 
                }
168
 
            }
169
 
            if (name == "connection") {
170
 
                if (value.find("keep-alive", 0) != string::npos) {
171
 
                    _keepalive = true;
172
 
                }
173
 
            }
174
 
            if (name == "content-length") {
175
 
                _filesize = strtol(value.c_str(), NULL, 0);
176
 
                log_debug("Setting Content Length to %d", _filesize);
177
 
            }
178
 
            if (name == "content-type") {
179
 
                // This is the type used by flash when sending a AMF data via POST
180
 
                if (value == "application/x-amf") {
181
 
//                  log_debug("Got AMF data in the POST request!");
182
 
                    _filetype = DiskStream::FILETYPE_AMF;
183
 
                }
184
 
                // This is the type used by wget when sending a file via POST
185
 
                if (value == "application/x-www-form-urlencoded") {
186
 
//                  log_debug("Got file data in the POST request");
187
 
                    _filetype = DiskStream::FILETYPE_ENCODED;
188
 
                }
189
 
                log_debug("Setting Content Type to %d", _filetype);
190
 
            }
191
 
            
192
 
//          cerr << "FIXME: " << (void *)i << " : " << dec <<  end << endl;
193
 
        } else {
194
 
            const boost::uint8_t *cmd = reinterpret_cast<const boost::uint8_t *>(i->c_str());
195
 
            if (extractCommand(const_cast<boost::uint8_t *>(cmd)) == HTTP::HTTP_NONE) {
196
 
                break;
197
 
#if 1
198
 
            } else {
199
 
                log_debug("Got a request, parsing \"%s\"", *i);
200
 
                string::size_type start = i->find(" ");
201
 
                string::size_type params = i->find("?");
202
 
                string::size_type pos = i->find("HTTP/");
203
 
                if (pos != string::npos) {
204
 
                    // The version is the last field and is the protocol name
205
 
                    // followed by a slash, and the version number. Note that
206
 
                    // the version is not a double, even though it has a dot
207
 
                    // in it. It's actually two separate integers.
208
 
                    _version.major = i->at(pos+5) - '0';
209
 
                    _version.minor = i->at(pos+7) - '0';
210
 
                    // log_debug (_("Version: %d.%d"), _version.major, _version.minor);
211
 
                    // the filespec in the request is the middle field, deliminated
212
 
                    // by a space on each end.
213
 
                    if (params != string::npos) {
214
 
                        _params = i->substr(params+1, end);
215
 
                        _filespec = i->substr(start+1, params);
216
 
                        log_debug("Parameters for file: \"%s\"", _params);
217
 
                    } else {
218
 
                        _filespec = i->substr(start+1, pos-start-2);
219
 
                    }
220
 
                    log_debug("Requesting file: \"%s\"", _filespec);
221
 
 
222
 
                    // HTTP 1.1 enables persistant network connections
223
 
                    // by default.
224
 
                    if (_version.minor > 0) {
225
 
                        log_debug("Enabling Keep Alive by default for HTTP > 1.0");
226
 
                        _keepalive = true;
227
 
                    }
228
 
                }
229
 
            }
230
 
#endif
231
 
        }
232
 
    }
233
 
    
234
 
    return buf->reference() + end + 4;
235
 
}
236
 
 
237
 
// // Parse an Echo Request message coming from the Red5 echo_test. This
238
 
// // method should only be used for testing purposes.
239
 
// vector<boost::shared_ptr<cygnal::Element > >
240
 
// HTTP::parseEchoRequest(boost::uint8_t *data, size_t size)
241
 
// {
242
 
// //    GNASH_REPORT_FUNCTION;
243
 
    
244
 
//     vector<boost::shared_ptr<cygnal::Element > > headers;
245
 
        
246
 
//     // skip past the header bytes, we don't care about them.
247
 
//     boost::uint8_t *tmpptr = data + 6;
248
 
    
249
 
//     boost::uint16_t length;
250
 
//     length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
251
 
//     tmpptr += sizeof(boost::uint16_t);
252
 
 
253
 
//     // Get the first name, which is a raw string, and not preceded by
254
 
//     // a type byte.
255
 
//     boost::shared_ptr<cygnal::Element > el1(new cygnal::Element);
256
 
    
257
 
//     // If the length of the name field is corrupted, then we get out of
258
 
//     // range quick, and corrupt memory. This is a bit of a hack, but
259
 
//     // reduces memory errors caused by some of the corrupted tes cases.
260
 
//     boost::uint8_t *endstr = std::find(tmpptr, tmpptr+length, '\0');
261
 
//     if (endstr != tmpptr+length) {
262
 
//      log_debug("Caught corrupted string! length was %d, null at %d",
263
 
//                length,  endstr-tmpptr);
264
 
//      length = endstr-tmpptr;
265
 
//     }
266
 
//     el1->setName(tmpptr, length);
267
 
//     tmpptr += length;
268
 
//     headers.push_back(el1);
269
 
    
270
 
//     // Get the second name, which is a raw string, and not preceded by
271
 
//     // a type byte.
272
 
//     length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
273
 
//     tmpptr += sizeof(boost::uint16_t);
274
 
//     boost::shared_ptr<cygnal::Element > el2(new cygnal::Element);
275
 
 
276
 
// //     std::string name2(reinterpret_cast<const char *>(tmpptr), length);
277
 
// //     el2->setName(name2.c_str(), name2.size());
278
 
//     // If the length of the name field is corrupted, then we get out of
279
 
//     // range quick, and corrupt memory. This is a bit of a hack, but
280
 
//     // reduces memory errors caused by some of the corrupted tes cases.
281
 
//     endstr = std::find(tmpptr, tmpptr+length, '\0');
282
 
//     if (endstr != tmpptr+length) {
283
 
//      log_debug("Caught corrupted string! length was %d, null at %d",
284
 
//                length,  endstr-tmpptr);
285
 
//      length = endstr-tmpptr;
286
 
//     }
287
 
//     el2->setName(tmpptr, length);
288
 
//     headers.push_back(el2);
289
 
//     tmpptr += length;
290
 
 
291
 
//     // Get the last two pieces of data, which are both AMF encoded
292
 
//     // with a type byte.
293
 
//     amf::AMF amf;
294
 
//     boost::shared_ptr<cygnal::Element> el3 = amf.extractAMF(tmpptr, tmpptr + size);
295
 
//     headers.push_back(el3);
296
 
//     tmpptr += amf.totalsize();
297
 
    
298
 
//     boost::shared_ptr<cygnal::Element> el4 = amf.extractAMF(tmpptr, tmpptr + size);
299
 
//     headers.push_back(el4);
300
 
 
301
 
//      return headers;
302
 
// }
303
 
 
304
 
// // format a response to the 'echo' test used for testing Gnash. This
305
 
// // is only used for testing by developers. The format appears to be
306
 
// // two strings, followed by a double, followed by the "onResult".
307
 
// cygnal::Buffer &
308
 
// HTTP::formatEchoResponse(const std::string &num, cygnal::Element &el)
309
 
// {
310
 
// //    GNASH_REPORT_FUNCTION;
311
 
//     boost::shared_ptr<cygnal::Buffer> data;
312
 
 
313
 
//     cygnal::Element nel;
314
 
//     if (el.getType() == cygnal::Element::TYPED_OBJECT_AMF0) {
315
 
//      nel.makeTypedObject();
316
 
//      string name = el.getName();
317
 
//      nel.setName(name);
318
 
//      if (el.propertySize()) {
319
 
//          // FIXME: see about using std::reverse() instead.
320
 
//          for (int i=el.propertySize()-1; i>=0; i--) {
321
 
// //       for (int i=0 ; i<el.propertySize(); i++) {
322
 
//              boost::shared_ptr<cygnal::Element> child = el.getProperty(i);
323
 
//              nel.addProperty(child);
324
 
//          }
325
 
//          data = nel.encode();
326
 
//      } else {
327
 
//          data = el.encode();
328
 
//      }
329
 
//     } else {
330
 
//      data = el.encode();
331
 
//     }
332
 
 
333
 
//     return formatEchoResponse(num, data->reference(), data->allocated());
334
 
// }
335
 
 
336
 
#if 0                           // FIXME:
337
 
// Client side parsing of response message codes
338
 
boost::shared_ptr<HTTP::http_response_t> 
339
 
HTTP::parseStatus(const std::string &line)
340
 
{
341
 
//    GNASH_REPORT_FUNCTION;
342
 
 
343
 
    boost::shared_ptr<http_response_t> status;
344
 
    // The respnse is a number followed by the error message.
345
 
    string::size_type pos = line.find(" ", 0);
346
 
    if (pos != string::npos) {
347
 
        status.reset(new http_response_t);
348
 
        status->code = static_cast<HTTP::http_status_e>(strtol(line.substr(0, pos).c_str(), NULL, 0));
349
 
        status->msg = line.substr(pos+1, line.size());
350
 
    }
351
 
 
352
 
    return status;
353
 
}
354
 
 
355
 
HTTP::http_method_e
356
 
HTTP::processClientRequest(int fd)
357
 
{
358
 
//    GNASH_REPORT_FUNCTION;
359
 
    bool result = false;
360
 
    
361
 
    boost::shared_ptr<cygnal::Buffer> buf(_que.peek());
362
 
    if (buf) {
363
 
        _cmd = extractCommand(buf->reference());
364
 
        switch (_cmd) {
365
 
          case HTTP::HTTP_GET:
366
 
              result = processGetRequest(fd);
367
 
              break;
368
 
          case HTTP::HTTP_POST:
369
 
              result = processPostRequest(fd);
370
 
              break;
371
 
          case HTTP::HTTP_HEAD:
372
 
              result = processHeadRequest(fd);
373
 
              break;
374
 
          case HTTP::HTTP_CONNECT:
375
 
              result = processConnectRequest(fd);
376
 
              break;
377
 
          case HTTP::HTTP_TRACE:
378
 
              result = processTraceRequest(fd);
379
 
              break;
380
 
          case HTTP::HTTP_OPTIONS:
381
 
              result = processOptionsRequest(fd);
382
 
              break;
383
 
          case HTTP::HTTP_PUT:
384
 
              result = processPutRequest(fd);
385
 
              break;
386
 
          case HTTP::HTTP_DELETE:
387
 
              result = processDeleteRequest(fd);
388
 
              break;
389
 
          default:
390
 
              break;
391
 
        }
392
 
    }
393
 
 
394
 
    return _cmd;
395
 
}
396
 
 
397
 
// A GET request asks the server to send a file to the client
398
 
bool
399
 
HTTP::processGetRequest(int fd)
400
 
{
401
 
    GNASH_REPORT_FUNCTION;
402
 
 
403
 
//     boost::uint8_t buffer[readsize+1];
404
 
//     const char *ptr = reinterpret_cast<const char *>(buffer);
405
 
//     memset(buffer, 0, readsize+1);
406
 
    
407
 
//    _handler->wait();
408
 
//    _handler->dump();
409
 
 
410
 
    cerr << "QUE = " << _que.size() << endl;
411
 
 
412
 
    if (_que.size() == 0) {
413
 
        return false;
414
 
    }
415
 
    
416
 
    boost::shared_ptr<cygnal::Buffer> buf(_que.pop());
417
 
//    cerr << "YYYYYYY: " << (char *)buf->reference() << endl;
418
 
//    cerr << hexify(buf->reference(), buf->allocated(), false) << endl;
419
 
    
420
 
    if (buf == 0) {
421
 
     // log_debug("Que empty, net connection dropped for fd #%d", getFileFd());
422
 
        log_debug("Que empty, net connection dropped for fd #%d", fd);
423
 
        return false;
424
 
    }
425
 
    
426
 
    clearHeader();
427
 
    processHeaderFields(*buf);
428
 
 
429
 
    string url = _docroot + _filespec;
430
 
    // See if the file is in the cache and already opened.
431
 
    boost::shared_ptr<DiskStream> filestream(cache.findFile(url));
432
 
    if (filestream) {
433
 
        log_network("FIXME: found file in cache!");
434
 
    } else {
435
 
        filestream.reset(new DiskStream);
436
 
//          cerr << "New Filestream at 0x" << hex << filestream.get() << endl;
437
 
        
438
 
//          cache.addFile(url, filestream);     FIXME: always reload from disk for now.
439
 
        
440
 
        // Oopen the file and read the first chunk into memory
441
 
        if (filestream->open(url)) {
442
 
            formatErrorResponse(HTTP::NOT_FOUND);
443
 
        } else {
444
 
            // Get the file size for the HTTP header
445
 
            if (filestream->getFileType() == DiskStream::FILETYPE_NONE) {
446
 
                formatErrorResponse(HTTP::NOT_FOUND);
447
 
            } else {
448
 
                cache.addPath(_filespec, filestream->getFilespec());
449
 
            }
450
 
        }
451
 
    }
452
 
    
453
 
    // Send the reply
454
 
    cygnal::Buffer &reply = formatHeader(filestream->getFileType(),
455
 
                                          filestream->getFileSize(),
456
 
                                          HTTP::OK);
457
 
    writeNet(fd, reply);
458
 
 
459
 
    size_t filesize = filestream->getFileSize();
460
 
    size_t bytes_read = 0;
461
 
    int ret;
462
 
    size_t page = 0;
463
 
    if (filesize) {
464
 
#ifdef USE_STATS_CACHE
465
 
        struct timespec start;
466
 
        clock_gettime (CLOCK_REALTIME, &start);
467
 
#endif
468
 
        size_t getbytes = 0;
469
 
        if (filesize <= filestream->getPagesize()) {
470
 
            getbytes = filesize;
471
 
        } else {
472
 
            getbytes = filestream->getPagesize();
473
 
        }
474
 
        if (filesize >= CACHE_LIMIT) {
475
 
            do {
476
 
                filestream->loadToMem(page);
477
 
                ret = writeNet(fd, filestream->get(), getbytes);
478
 
                if (ret <= 0) {
479
 
                    break;
480
 
                }
481
 
                bytes_read += ret;
482
 
                page += filestream->getPagesize();
483
 
            } while (bytes_read <= filesize);
484
 
        } else {
485
 
            filestream->loadToMem(filesize, 0);
486
 
            ret = writeNet(fd, filestream->get(), filesize);
487
 
        }
488
 
        filestream->close();
489
 
#ifdef USE_STATS_CACHE
490
 
        struct timespec end;
491
 
        clock_gettime (CLOCK_REALTIME, &end);
492
 
        double time = (end.tv_sec - start.tv_sec) + ((end.tv_nsec - start.tv_nsec)/1e9);
493
 
        cerr << "File " << _filespec
494
 
             << " transferred " << filesize << " bytes in: " << fixed
495
 
             << time << " seconds for net fd #" << fd << endl;
496
 
#endif
497
 
    }
498
 
 
499
 
    log_debug("http_handler all done transferring requested file \"%s\".", _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
 
    boost::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
 
    boost::uint8_t *data = processHeaderFields(*buf);
527
 
    size_t length = strtol(getField("content-length").c_str(), NULL, 0);
528
 
    boost::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
 
        boost::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<boost::shared_ptr<cygnal::Element> > headers = parseEchoRequest(*content);
567
 
        //boost::shared_ptr<cygnal::Element> &el0 = headers[0];
568
 
        //boost::shared_ptr<cygnal::Element> &el1 = headers[1];
569
 
        //boost::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
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(), NULL, 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
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
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
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
 
boost::shared_ptr<std::vector<std::string> >
735
 
HTTP::getFieldItem(const std::string &name)
736
 
{
737
 
//    GNASH_REPORT_FUNCTION;
738
 
    boost::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
748
 
HTTP::startHeader()
749
 
{
750
 
//    GNASH_REPORT_FUNCTION;
751
 
 
752
 
    clearHeader();
753
 
    
754
 
    return true;
755
 
}
756
 
 
757
 
cygnal::Buffer &
758
 
HTTP::formatHeader(http_status_e type)
759
 
{
760
 
//    GNASH_REPORT_FUNCTION;
761
 
 
762
 
    return formatHeader(_filesize, type);
763
 
}
764
 
 
765
 
cygnal::Buffer &
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 &
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 &
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 &
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 &
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 &
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 &
1015
 
HTTP::formatContentLength()
1016
 
{
1017
 
//    GNASH_REPORT_FUNCTION;
1018
 
    
1019
 
    return formatContentLength(_filesize);
1020
 
}
1021
 
 
1022
 
cygnal::Buffer &
1023
 
HTTP::formatContentLength(boost::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 &
1038
 
HTTP::formatContentType()
1039
 
{
1040
 
//    GNASH_REPORT_FUNCTION;
1041
 
    return formatContentType(_filetype);
1042
 
}
1043
 
 
1044
 
cygnal::Buffer &
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 &
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 &
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 &
1142
 
HTTP::formatEchoResponse(const std::string &num, boost::uint8_t *data, size_t size)
1143
 
{
1144
 
//    GNASH_REPORT_FUNCTION;
1145
 
 
1146
 
    //boost::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
 
    boost::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
 
    boost::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 &
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
1253
 
HTTP::extractCommand(boost::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
 
        boost::uint8_t *start = std::find(data, data+7, ' ') + 1;
1289
 
        boost::uint8_t *end   = std::find(start + 2, data+PATH_MAX, ' ');
1290
 
        boost::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
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
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
1351
 
HTTP::sendMsg(const boost::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
1360
 
HTTP::recvChunked(boost::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
 
    boost::shared_ptr<cygnal::Buffer> buf;
1378
 
    boost::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(), NULL, 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...", getFileFd());
1419
 
                done = true;
1420
 
            }
1421
 
            if (ret) {
1422
 
                // manually set the seek pointer in the buffer, as we read
1423
 
                // the data into the raw memory allocated to the buffer. We
1424
 
                // only want to do this if we got data of course.
1425
 
                buf->setSeekPointer(buf->end() + ret);
1426
 
                if (!total) {
1427
 
                    start = std::find(buf->reference(), buf->reference()+ret, '\r') + 2;
1428
 
                    if (start != buf->reference()+ret) {
1429
 
                        // extract the total size of the chunk
1430
 
                        std::string bytes(buf->reference(), start-2);
1431
 
                        total = static_cast<size_t>(strtol(bytes.c_str(), NULL, 16));
1432
 
                        // The total size of the last chunk is always "0"
1433
 
                        if (total == 0) {
1434
 
                            log_debug("%s: end of chunks!", __PRETTY_FUNCTION__);
1435
 
                            pktsize = 0;
1436
 
                            done = true;
1437
 
                            chunks = false;
1438
 
                        } else {
1439
 
                            pktsize = total+8; // FIXME: why do we need an 8 here ?
1440
 
//                          log_debug("%s: Total size for chunk is: %d (%s), adding %d bytes",
1441
 
//                                    __PRETTY_FUNCTION__, total, bytes, (start - buf->reference()));
1442
 
                            cygnal::Buffer tmpbuf(start - buf->reference());
1443
 
                            // don't forget the two bytes for the "\r\n"
1444
 
                            tmpbuf.copy(buf->reference() + bytes.size() + 2, (start - buf->reference()));
1445
 
                            buf->clear(); // FIXME: debug only
1446
 
                            buf->resize(total);
1447
 
                            buf->copy(tmpbuf.reference(), tmpbuf.size());
1448
 
                        }
1449
 
                    }
1450
 
                }
1451
 
                
1452
 
                // If we got less data than specified, we need to keep reading data
1453
 
                if (ret < buf->size()) {
1454
 
                    pktsize -= ret;
1455
 
                    if (pktsize == 0) {
1456
 
                        done = true;
1457
 
                    } else {
1458
 
//                      log_debug("Didn't get a full packet, reading more data");
1459
 
                        continue;
1460
 
                    }
1461
 
                }
1462
 
            }
1463
 
        } while (!done);
1464
 
//      log_debug("%s: finished packet, ret is %d, pktsize is %d\r\n%s", __PRETTY_FUNCTION__, ret, pktsize,
1465
 
//                hexify(buf->reference(), buf->allocated(), true));
1466
 
        if (pktsize == 0) {
1467
 
            // The last two bytes of the chunk are always "\r\n", which we remove so it
1468
 
            // doesn't become part of the binary data being sent in the chunk.
1469
 
            if ((*(buf->end()-2) == '\r') && (*(buf->end()-1) == '\n')) {
1470
 
                *(buf->end()-2) = 0; // '\r'
1471
 
                *(buf->end()-1) = 0; // '\n'
1472
 
                buf->setSeekPointer(buf->end() - 2);
1473
 
            }
1474
 
            _que.push(buf);
1475
 
        }
1476
 
        done = false;           // reset
1477
 
    } // end of while chunks
1478
 
    
1479
 
    return _que.size();
1480
 
}
1481
 
 
1482
 
int
1483
 
HTTP::recvMsg(int fd)
1484
 
{
1485
 
//     GNASH_REPORT_FUNCTION;
1486
 
    return recvMsg(fd, 0);
1487
 
}
1488
 
 
1489
 
int
1490
 
HTTP::recvMsg(int fd, size_t size)
1491
 
{
1492
 
    // GNASH_REPORT_FUNCTION;
1493
 
 
1494
 
    size_t ret = 0;
1495
 
 
1496
 
    if (size == 0) {
1497
 
        size = cygnal::NETBUFSIZE;
1498
 
    }
1499
 
    
1500
 
    log_debug("Starting to wait for data in net for fd #%d", fd);
1501
 
    Network net;
1502
 
 
1503
 
    do {
1504
 
        boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(size));
1505
 
        ret = net.readNet(fd, *buf, 5);
1506
 
//      cerr << __PRETTY_FUNCTION__ << ret << " : " << (char *)buf->reference() << endl;
1507
 
 
1508
 
        // the read timed out as there was no data, but the socket is still open.
1509
 
        if (ret == 0) {
1510
 
            log_debug("no data yet for fd #%d, continuing...", fd);
1511
 
            continue;
1512
 
        }
1513
 
        // ret is "no position" when the socket is closed from the other end of the connection,
1514
 
        // so we're done.
1515
 
        if ((ret == static_cast<size_t>(string::npos)) || (static_cast<int>(ret) == -1)) {
1516
 
            log_debug("socket for fd #%d was closed...", fd);
1517
 
            return 0;
1518
 
        }
1519
 
        // We got data. Resize the buffer if necessary.
1520
 
        if (ret > 0) {
1521
 
            buf->setSeekPointer(buf->reference() + ret);
1522
 
//          cerr << "XXXXX: " << (char *)buf->reference() << endl;
1523
 
            if (ret < static_cast<int>(cygnal::NETBUFSIZE)) {
1524
 
//              buf->resize(ret);       FIXME: why does this corrupt
1525
 
//              the buffer ?
1526
 
                _que.push(buf);
1527
 
                break;
1528
 
            } else {
1529
 
                _que.push(buf);
1530
 
            }
1531
 
            // ret must be more than 0 here
1532
 
            if (ret == buf->size()) {
1533
 
                continue;
1534
 
            }
1535
 
        } else {
1536
 
            log_debug("no more data for fd #%d, exiting...", fd);
1537
 
            return 0;
1538
 
        }
1539
 
        if (static_cast<int>(ret) == -1) {
1540
 
          log_debug("Handler done for fd #%d, can't read any data...", fd);
1541
 
          return -1;
1542
 
        }
1543
 
    } while (ret);
1544
 
    
1545
 
    // We're done. Notify the other threads the socket is closed, and tell them to die.
1546
 
    log_debug("Done receiving data for fd #%d...", fd);
1547
 
 
1548
 
    return ret;
1549
 
}
1550
 
 
1551
 
 
1552
 
void
1553
 
HTTP::dump() {
1554
 
//    GNASH_REPORT_FUNCTION;
1555
 
    
1556
 
    boost::mutex::scoped_lock lock(stl_mutex);
1557
 
        
1558
 
    log_debug (_("==== The HTTP header breaks down as follows: ===="));
1559
 
    log_debug (_("Filespec: %s"), _filespec.c_str());
1560
 
    log_debug (_("Version: %d.%d"), _version.major, _version.minor);
1561
 
 
1562
 
    std::map<string, string>::const_iterator it;
1563
 
    for (it = _fields.begin(); it != _fields.end(); ++it) {
1564
 
        log_debug("Field: \"%s\" = \"%s\"", it->first, it->second);
1565
 
    }
1566
 
    
1567
 
    // Dump the RTMPT fields
1568
 
    log_debug("RTMPT optional index is: ", _index);
1569
 
    log_debug("RTMPT optional client ID is: ", _clientid);
1570
 
    log_debug (_("==== ==== ===="));
1571
 
}
1572
 
 
1573
 
} // end of gnash namespace
1574
 
 
1575
 
 
1576
 
// local Variables:
1577
 
// mode: C++
1578
 
// indent-tabs-mode: t
1579
 
// End: