~vjsamuel/drizzle/bug-616035

« back to all changes in this revision

Viewing changes to plugin/pbms/src/cslib/CSHTTPStream.cc

  • Committer: Monty Taylor
  • Date: 2010-07-06 00:44:32 UTC
  • mfrom: (1643.1.13 build)
  • Revision ID: mordred@inaugust.com-20100706004432-843uftc92rc2497l
Merged in PBMS, translation updates, a few build fixes and a few bug fixes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2008 PrimeBase Technologies GmbH, Germany
 
2
 *
 
3
 * PrimeBase Media Stream for MySQL
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
18
 *
 
19
 * Original author: Paul McCullagh (H&G2JCtL)
 
20
 * Continued development: Barry Leslie
 
21
 *
 
22
 * 2007-06-10
 
23
 *
 
24
 * A basic syncronized object.
 
25
 *
 
26
 */
 
27
#include "CSConfig.h"
 
28
 
 
29
#include <stdlib.h>
 
30
#include <inttypes.h>
 
31
 
 
32
#include "string.h"
 
33
 
 
34
#include "CSHTTPStream.h"
 
35
#include "CSGlobal.h"
 
36
 
 
37
#ifdef DEBUG
 
38
//#define PRINT_HEADER
 
39
#endif
 
40
 
 
41
/*
 
42
 * ---------------------------------------------------------------
 
43
 * HTTP HEADERS
 
44
 */
 
45
 
 
46
CSHeader::~CSHeader()
 
47
{
 
48
        if (iName) {
 
49
                iName->release();
 
50
                iName = NULL;
 
51
        }
 
52
        if (iValue) {
 
53
                iValue->release();
 
54
                iValue = NULL;
 
55
        }
 
56
}
 
57
 
 
58
void CSHeader::setName(const char *name)
 
59
{
 
60
        iName = CSString::newString(name);
 
61
}
 
62
 
 
63
void CSHeader::setName(const char *name, uint32_t len)
 
64
{
 
65
        iName = CSString::newString(name, len);
 
66
}
 
67
 
 
68
void CSHeader::setName(CSString *name)
 
69
{
 
70
        iName = name;
 
71
}
 
72
 
 
73
void CSHeader::setValue(const char *value)
 
74
{
 
75
        iValue = CSString::newString(value);
 
76
}
 
77
 
 
78
void CSHeader::setValue(const char *value, uint32_t len)
 
79
{
 
80
        iValue = CSString::newString(value, len);
 
81
}
 
82
 
 
83
void CSHeader::setValue(CSString *value)
 
84
{
 
85
        iValue = value;
 
86
}
 
87
 
 
88
void CSHeader::write(CSOutputStream *out)
 
89
{
 
90
        out->print(iName);
 
91
        out->print(": ");
 
92
        out->print(iValue);
 
93
        out->print("\r\n");
 
94
}
 
95
 
 
96
void CSHTTPHeaders::clearHeaders()
 
97
{
 
98
        iKeepAlive = false;
 
99
        iExpect100Continue = false;
 
100
        iUnknownEpectHeader = false;
 
101
        if (iHeaders) {
 
102
                iHeaders->release();
 
103
                iHeaders = NULL;
 
104
        }
 
105
}
 
106
CSVector *CSHTTPHeaders::takeHeaders()
 
107
{
 
108
        CSVector *headers = iHeaders;
 
109
        iHeaders = NULL;
 
110
        return headers;
 
111
}
 
112
 
 
113
void CSHTTPHeaders::setHeaders(CSVector *headers)
 
114
{
 
115
        if (iHeaders) 
 
116
                iHeaders->release();
 
117
        iHeaders = headers;
 
118
}
 
119
 
 
120
void CSHTTPHeaders::addHeader(CSHeader *h)
 
121
{
 
122
        if (!iHeaders)
 
123
                new_(iHeaders, CSVector(5));
 
124
 
 
125
        if (strcasecmp(h->getNameCString(), "Connection") == 0 && strcasecmp(h->getValueCString(), "Keep-Alive") == 0)
 
126
                iKeepAlive = true;
 
127
                
 
128
        if (strcasecmp(h->getNameCString(), "Expect") == 0) {
 
129
                if (strcasecmp(h->getValueCString(), "100-continue") == 0)
 
130
                        iExpect100Continue = true;
 
131
                else
 
132
                        iUnknownEpectHeader = true;
 
133
        }
 
134
                
 
135
        iHeaders->add(h);
 
136
}
 
137
 
 
138
void CSHTTPHeaders::addHeaders(CSHTTPHeaders *headers)
 
139
{
 
140
        CSHeader *h;
 
141
        uint32_t i =0;
 
142
        while ((h = headers->getHeader(i++))) {
 
143
                addHeader(h);
 
144
        }
 
145
}
 
146
 
 
147
void CSHTTPHeaders::addHeader(const char *name, const char *value)
 
148
{
 
149
        CSHeader *h;
 
150
 
 
151
        enter_();
 
152
        if (!iHeaders)
 
153
                new_(iHeaders, CSVector(5));
 
154
 
 
155
        new_(h, CSHeader());
 
156
        push_(h);
 
157
        h->setName(name);
 
158
        h->setValue(value);
 
159
        pop_(h);
 
160
 
 
161
        addHeader(h);
 
162
        exit_();
 
163
}
 
164
 
 
165
void CSHTTPHeaders::addHeader(const char *name, uint32_t nlen, const char *value, uint32_t vlen)
 
166
{
 
167
        CSHeader *h;
 
168
 
 
169
        enter_();
 
170
        if (!iHeaders)
 
171
                new_(iHeaders, CSVector(5));
 
172
 
 
173
        new_(h, CSHeader());
 
174
        push_(h);
 
175
        h->setName(name, nlen);
 
176
        h->setValue(value, vlen);
 
177
        pop_(h);
 
178
        addHeader(h);
 
179
        exit_();
 
180
}
 
181
 
 
182
void CSHTTPHeaders::addHeader(CSString *name, CSString *value)
 
183
{
 
184
        CSHeader *h;
 
185
 
 
186
        enter_();
 
187
        push_(name);
 
188
        push_(value);
 
189
        if (!iHeaders)
 
190
                new_(iHeaders, CSVector(5));
 
191
 
 
192
        new_(h, CSHeader());
 
193
        pop_(value);
 
194
        pop_(name);
 
195
        h->setName(name);
 
196
        h->setValue(value);
 
197
        addHeader(h);
 
198
        exit_();
 
199
}
 
200
 
 
201
void CSHTTPHeaders::addHeader(const char *name, CSString *value)
 
202
{
 
203
        CSHeader *h;
 
204
        CSString *n;
 
205
 
 
206
        enter_();
 
207
        push_(value);
 
208
        n = CSString::newString(name);
 
209
        push_(n);
 
210
        if (!iHeaders)
 
211
                new_(iHeaders, CSVector(5));
 
212
        new_(h, CSHeader());
 
213
        pop_(n);
 
214
        pop_(value);
 
215
        h->setName(n);
 
216
        h->setValue(value);
 
217
        addHeader(h);
 
218
        exit_();
 
219
}
 
220
 
 
221
void CSHTTPHeaders::removeHeader(CSString *name)
 
222
{
 
223
        enter_();
 
224
        push_(name);
 
225
        if (iHeaders) {
 
226
                CSHeader *h;
 
227
 
 
228
                for (uint32_t i=0; i<iHeaders->size(); ) {
 
229
                        h = (CSHeader *) iHeaders->get(i);
 
230
                        if (h->getName()->compare(name) == 0) {
 
231
                                iHeaders->remove(i);
 
232
                        } else 
 
233
                                i++;
 
234
                }
 
235
        }
 
236
        release_(name);
 
237
        
 
238
        exit_();
 
239
}
 
240
 
 
241
void CSHTTPHeaders::removeHeader(const char *name)
 
242
{
 
243
        removeHeader(CSString::newString(name));
 
244
}
 
245
 
 
246
CSString *CSHTTPHeaders::getHeaderValue(const char *name)
 
247
{
 
248
        CSString *n;
 
249
        CSString *v;
 
250
 
 
251
        enter_();
 
252
        n = CSString::newString(name);
 
253
        push_(n);
 
254
        v = NULL;
 
255
        if (iHeaders) {
 
256
                CSHeader *h;
 
257
 
 
258
                for (uint32_t i=0; i<iHeaders->size(); i++) {
 
259
                        h = (CSHeader *) iHeaders->get(i);
 
260
                        if (h->getName()->compare(n) == 0) {
 
261
                                v = h->getValue();
 
262
                                v->retain();
 
263
                                break;
 
264
                        }
 
265
                }
 
266
        }
 
267
        release_(n);
 
268
        return_(v);
 
269
}
 
270
 
 
271
void CSHTTPHeaders::writeHeader(CSOutputStream *out)
 
272
{
 
273
        if (iHeaders) {
 
274
                CSHeader *h;
 
275
 
 
276
                for (uint32_t i=0; i<iHeaders->size(); i++) {
 
277
                        h = (CSHeader *) iHeaders->get(i);
 
278
                        h->write(out);
 
279
                }
 
280
        }
 
281
}
 
282
 
 
283
bool CSHTTPHeaders::keepAlive()
 
284
{
 
285
        return iKeepAlive;
 
286
}
 
287
 
 
288
bool CSHTTPHeaders::expect100Continue()
 
289
{
 
290
        return iExpect100Continue;
 
291
}
 
292
 
 
293
bool CSHTTPHeaders::unknownEpectHeader()
 
294
{
 
295
        return iUnknownEpectHeader;
 
296
}
 
297
 
 
298
/*
 
299
 * ---------------------------------------------------------------
 
300
 * HTTP INPUT STREAMS
 
301
 */
 
302
 
 
303
CSHTTPInputStream::CSHTTPInputStream(CSInputStream* in):
 
304
CSHTTPHeaders(),
 
305
iInput(NULL),
 
306
iMethod(NULL),
 
307
iRequestURI(NULL),
 
308
iHTTPVersion(NULL),
 
309
iStatusPhrase(NULL)
 
310
{
 
311
        iInput = in;
 
312
}
 
313
 
 
314
CSHTTPInputStream::~CSHTTPInputStream()
 
315
{
 
316
        freeHead();
 
317
        if (iInput)
 
318
                iInput->release();
 
319
}
 
320
 
 
321
void CSHTTPInputStream::readHead()
 
322
{
 
323
        CSStringBuffer  *sb = NULL;
 
324
        bool                    first_line = true;
 
325
        uint32_t                        start, end;
 
326
 
 
327
        enter_();
 
328
        freeHead();
 
329
        for (;;) {
 
330
                sb = iInput->readLine();
 
331
                if (!sb)
 
332
                        break;
 
333
#ifdef PRINT_HEADER
 
334
                printf("HTTP: %s\n", sb->getCString());
 
335
#endif
 
336
                if (sb->length() == 0) {
 
337
                        sb->release();
 
338
                        break;
 
339
                }
 
340
                push_(sb);
 
341
                
 
342
                if (first_line) {
 
343
                        CSString *str;
 
344
                        start = sb->ignore(0, ' ');
 
345
                        end = sb->find(start, ' ');
 
346
                        str = sb->substr(start, end - start);
 
347
                        if (str->startsWith("HTTP")) { // Reply header
 
348
                                iMethod = NULL;
 
349
                                iRequestURI = NULL;
 
350
                                iHTTPVersion = str;
 
351
                                start = sb->ignore(end, ' ');
 
352
                                end = sb->find(start, ' ');
 
353
                                if (start > end)
 
354
                                        CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
 
355
 
 
356
                                str = sb->substr(start, end - start);
 
357
                                iStatus = atol(str->getCString());
 
358
                                str->release();
 
359
                                start = sb->ignore(end, ' ');
 
360
                                end = sb->find(start, '\r');
 
361
                                if (start > end)
 
362
                                        CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
 
363
                                iStatusPhrase = sb->substr(start, end - start);
 
364
                        } else {
 
365
                                iStatus = 0;
 
366
                                iStatusPhrase = NULL;
 
367
                                iMethod = str;
 
368
                        start = sb->ignore(end, ' ');
 
369
                        end = sb->find(start, ' ');
 
370
                        if (start > end)
 
371
                                CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
 
372
                        iRequestURI = sb->substr(start, end - start);
 
373
                        start = sb->ignore(end, ' ');
 
374
                        end = sb->find(start, ' ');
 
375
                        if (start > end)
 
376
                                CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
 
377
                        iHTTPVersion = sb->substr(start, end - start);
 
378
                        }                               
 
379
                        first_line = false;
 
380
                }
 
381
                else {
 
382
                        uint32_t nstart, nend;
 
383
                        uint32_t vstart, vend;
 
384
 
 
385
                        nstart = sb->ignore(0, ' ');
 
386
                        nend = sb->find(nstart, ':');
 
387
 
 
388
                        vstart = sb->ignore(nend+1, ' ');
 
389
                        vend = sb->find(vstart, '\r');
 
390
 
 
391
                        nend = sb->trim(nend, ' ');
 
392
                        vend = sb->trim(vend, ' ');
 
393
                        
 
394
                        if (vstart > vend)
 
395
                                CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
 
396
                        addHeader(sb->getBuffer(nstart), nend-nstart, sb->getBuffer(vstart), vend-vstart);
 
397
                }
 
398
 
 
399
                release_(sb);
 
400
        }
 
401
        exit_();
 
402
}
 
403
 
 
404
bool CSHTTPInputStream::getRange(uint64_t *size, uint64_t *offset)
 
405
{
 
406
        CSString        *val;
 
407
        bool haveRange = false;
 
408
 
 
409
        if ((val = getHeaderValue("Range"))) {
 
410
                uint64_t                first_byte = 0, last_byte = 0;
 
411
                const char      *range = val->getCString();
 
412
                
 
413
                if (range && (val->compare("bytes=", 6) == 0)) {
 
414
                        if ((sscanf(range + 6, "%"PRIu64"-%"PRIu64"", &first_byte, &last_byte) == 2) && (last_byte >= first_byte)) {
 
415
                                *offset = (uint64_t) first_byte;
 
416
                                *size =last_byte - first_byte + 1;
 
417
                                haveRange = true;
 
418
                        }       
 
419
                }
 
420
                val->release();
 
421
                                
 
422
        }
 
423
        return haveRange;
 
424
}
 
425
 
 
426
uint64_t CSHTTPInputStream::getContentLength()
 
427
{
 
428
        CSString        *val;
 
429
        uint64_t                size = 0;
 
430
 
 
431
        if ((val = getHeaderValue("Content-Length"))) {
 
432
                const char      *len = val->getCString();
 
433
 
 
434
                if (len)  
 
435
                        sscanf(len, "%"PRIu64"", &size);                
 
436
                val->release();
 
437
        }
 
438
        return size;
 
439
}
 
440
 
 
441
const char *CSHTTPInputStream::getMethod()
 
442
{
 
443
        if (!iMethod)
 
444
                return NULL;
 
445
        return iMethod->getCString();
 
446
}
 
447
 
 
448
void CSHTTPInputStream::close()
 
449
{
 
450
        enter_();
 
451
        iInput->close();
 
452
        exit_();
 
453
}
 
454
 
 
455
size_t CSHTTPInputStream::read(char *b, size_t len)
 
456
{
 
457
        enter_();
 
458
        return_(iInput->read(b, len));
 
459
}
 
460
 
 
461
int CSHTTPInputStream::read()
 
462
{
 
463
        enter_();
 
464
        return_(iInput->read());
 
465
}
 
466
 
 
467
int CSHTTPInputStream::peek()
 
468
{
 
469
        enter_();
 
470
        return_(iInput->peek());
 
471
}
 
472
 
 
473
void CSHTTPInputStream::freeHead()
 
474
{
 
475
        enter_();
 
476
        clearHeaders();
 
477
        if (iMethod) {
 
478
                iMethod->release();
 
479
                iMethod = NULL;
 
480
        }
 
481
        if (iRequestURI) {
 
482
                iRequestURI->release();
 
483
                iRequestURI = NULL;
 
484
        }
 
485
        if (iHTTPVersion) {
 
486
                iHTTPVersion->release();
 
487
                iHTTPVersion = NULL;
 
488
        }
 
489
        if (iStatusPhrase) {
 
490
                iStatusPhrase->release();
 
491
                iStatusPhrase = NULL;
 
492
        }
 
493
        iStatus = 0;
 
494
        exit_();
 
495
}
 
496
 
 
497
CSHTTPInputStream *CSHTTPInputStream::newStream(CSInputStream* i)
 
498
{
 
499
        CSHTTPInputStream *s;
 
500
 
 
501
        if (!(s = new CSHTTPInputStream(i))) {
 
502
                i->release();
 
503
                CSException::throwOSError(CS_CONTEXT, ENOMEM);
 
504
        }
 
505
        return s;
 
506
}
 
507
 
 
508
/*
 
509
 * ---------------------------------------------------------------
 
510
 * HTTP OUTPUT STREAMS
 
511
 */
 
512
 
 
513
CSHTTPOutputStream::CSHTTPOutputStream(CSOutputStream* out):
 
514
CSHTTPHeaders(),
 
515
iOutput(NULL),
 
516
iStatus(0),
 
517
iContentLength(0),
 
518
iRangeSize(0),
 
519
iRangeOffset(0),
 
520
iTotalLength(0)
 
521
{
 
522
        iOutput = out;
 
523
        iBody.setGrowSize(120);
 
524
}
 
525
 
 
526
CSHTTPOutputStream::~CSHTTPOutputStream()
 
527
{
 
528
        clearHeaders();
 
529
        clearBody();
 
530
        if (iOutput)
 
531
                iOutput->release();
 
532
}
 
533
 
 
534
void CSHTTPOutputStream::writeHeaders()
 
535
{
 
536
        writeHeader(this);
 
537
        clearHeaders();
 
538
}
 
539
 
 
540
void CSHTTPOutputStream::writeHead()
 
541
{
 
542
        iOutput->print("HTTP/1.1 ");
 
543
        iOutput->print(iStatus);
 
544
        iOutput->print(" ");
 
545
        iOutput->print(getReasonPhrase(iStatus));
 
546
        iOutput->print("\r\n");
 
547
        writeHeader(iOutput);
 
548
        iOutput->print("Content-Length: ");
 
549
        iOutput->print(iContentLength);
 
550
        iOutput->print("\r\n");
 
551
        if (iRangeSize && (iStatus == 200)) {
 
552
                iOutput->print("Content-Range: bytes ");
 
553
                iOutput->print(iRangeOffset);
 
554
                iOutput->print("-");
 
555
                iOutput->print(iRangeOffset + iRangeSize -1);
 
556
                iOutput->print("/");
 
557
                iOutput->print(iTotalLength);
 
558
                iOutput->print("\r\n");
 
559
        }
 
560
        iOutput->print("\r\n");
 
561
}
 
562
 
 
563
void CSHTTPOutputStream::clearBody()
 
564
{
 
565
        iRangeSize = 0;
 
566
        iRangeOffset = 0;
 
567
        iTotalLength = 0;
 
568
        iContentLength = 0;
 
569
        iBody.clear();
 
570
}
 
571
 
 
572
void CSHTTPOutputStream::writeBody()
 
573
{
 
574
        iOutput->write(iBody.getBuffer(0), iBody.length());
 
575
}
 
576
 
 
577
void CSHTTPOutputStream::appendBody(const char *str)
 
578
{
 
579
        iBody.append(str);
 
580
        iContentLength = iBody.length();
 
581
}
 
582
 
 
583
void CSHTTPOutputStream::appendBody(int value)
 
584
{
 
585
        iBody.append(value);
 
586
        iContentLength = iBody.length();
 
587
}
 
588
 
 
589
void CSHTTPOutputStream::close()
 
590
{
 
591
        enter_();
 
592
        iOutput->close();
 
593
        exit_();
 
594
}
 
595
 
 
596
void CSHTTPOutputStream::write(const char *b, size_t len)
 
597
{
 
598
        enter_();
 
599
        iOutput->write(b, len);
 
600
        exit_();
 
601
}
 
602
 
 
603
void CSHTTPOutputStream::flush()
 
604
{
 
605
        enter_();
 
606
        iOutput->flush();
 
607
        exit_();
 
608
}
 
609
 
 
610
void CSHTTPOutputStream::write(char b)
 
611
{
 
612
        enter_();
 
613
        iOutput->write(b);
 
614
        exit_();
 
615
}
 
616
 
 
617
const char *CSHTTPOutputStream::getReasonPhrase(int code)
 
618
{
 
619
        const char *message = "Unknown Code";
 
620
 
 
621
        switch (code) {
 
622
                case 100: message = "Continue"; break;
 
623
                case 101: message = "Switching Protocols"; break;
 
624
                case 200: message = "OK"; break;
 
625
                case 201: message = "Created"; break;
 
626
                case 202: message = "Accepted"; break;
 
627
                case 203: message = "Non-Authoritative Information"; break;
 
628
                case 204: message = "No Content"; break;
 
629
                case 205: message = "Reset Content"; break;
 
630
                case 206: message = "Partial Content"; break;
 
631
                case 300: message = "Multiple Choices"; break;
 
632
                case 301: message = "Moved Permanently"; break;
 
633
                case 302: message = "Found"; break;
 
634
                case 303: message = "See Other"; break;
 
635
                case 304: message = "Not Modified"; break;
 
636
                case 305: message = "Use Proxy"; break;
 
637
                case 307: message = "Temporary Redirect"; break;
 
638
                case 400: message = "Bad Request"; break;
 
639
                case 401: message = "Unauthorized"; break;
 
640
                case 402: message = "Payment Required"; break;
 
641
                case 403: message = "Forbidden"; break;
 
642
                case 404: message = "Not Found"; break;
 
643
                case 405: message = "Method Not Allowed"; break;
 
644
                case 406: message = "Not Acceptable"; break;
 
645
                case 407: message = "Proxy Authentication Required"; break;
 
646
                case 408: message = "Request Time-out"; break;
 
647
                case 409: message = "Conflict"; break;
 
648
                case 410: message = "Gone"; break;
 
649
                case 411: message = "Length Required"; break;
 
650
                case 412: message = "Precondition Failed"; break;
 
651
                case 413: message = "Request Entity Too Large"; break;
 
652
                case 414: message = "Request-URI Too Large"; break;
 
653
                case 415: message = "Unsupported Media Type"; break;
 
654
                case 416: message = "Requested range not satisfiable"; break;
 
655
                case 417: message = "Expectation Failed"; break;
 
656
                case 500: message = "Internal Server Error"; break;
 
657
                case 501: message = "Not Implemented"; break;
 
658
                case 502: message = "Bad Gateway"; break;
 
659
                case 503: message = "Service Unavailable"; break;
 
660
                case 504: message = "Gateway Time-out"; break;
 
661
                case 505: message = "HTTP Version not supported"; break;
 
662
        }
 
663
        return message;
 
664
}
 
665
 
 
666
CSHTTPOutputStream *CSHTTPOutputStream::newStream(CSOutputStream* i)
 
667
{
 
668
        CSHTTPOutputStream *s;
 
669
 
 
670
        if (!(s = new CSHTTPOutputStream(i))) {
 
671
                i->release();
 
672
                CSException::throwOSError(CS_CONTEXT, ENOMEM);
 
673
        }
 
674
        return s;
 
675
}
 
676
 
 
677