1
/* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
3
* PrimeBase Media Stream for MySQL
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
* Original author: Paul McCullagh (H&G2JCtL)
20
* Continued development: Barry Leslie
24
* A basic syncronized object.
34
#include "CSHTTPStream.h"
38
//#define PRINT_HEADER
42
* ---------------------------------------------------------------
58
void CSHeader::setName(const char *name)
60
iName = CSString::newString(name);
63
void CSHeader::setName(const char *name, uint32_t len)
65
iName = CSString::newString(name, len);
68
void CSHeader::setName(CSString *name)
73
void CSHeader::setValue(const char *value)
75
iValue = CSString::newString(value);
78
void CSHeader::setValue(const char *value, uint32_t len)
80
iValue = CSString::newString(value, len);
83
void CSHeader::setValue(CSString *value)
88
void CSHeader::write(CSOutputStream *out, bool trace)
91
printf("%s: %s\n", iName->getCString(), iValue->getCString());
100
void CSHTTPHeaders::clearHeaders()
103
iExpect100Continue = false;
104
iUnknownEpectHeader = false;
110
CSVector *CSHTTPHeaders::takeHeaders()
112
CSVector *headers = iHeaders;
117
void CSHTTPHeaders::setHeaders(CSVector *headers)
124
void CSHTTPHeaders::addHeader(CSHeader *h)
127
new_(iHeaders, CSVector(5));
129
if (strcasecmp(h->getNameCString(), "Connection") == 0 && strcasecmp(h->getValueCString(), "Keep-Alive") == 0)
132
if (strcasecmp(h->getNameCString(), "Expect") == 0) {
133
if (strcasecmp(h->getValueCString(), "100-continue") == 0)
134
iExpect100Continue = true;
136
iUnknownEpectHeader = true;
142
void CSHTTPHeaders::addHeaders(CSHTTPHeaders *headers)
146
while ((h = headers->getHeader(i++))) {
151
void CSHTTPHeaders::addHeader(const char *name, const char *value)
157
new_(iHeaders, CSVector(5));
169
void CSHTTPHeaders::addHeader(const char *name, uint32_t nlen, const char *value, uint32_t vlen)
175
new_(iHeaders, CSVector(5));
179
h->setName(name, nlen);
180
h->setValue(value, vlen);
186
void CSHTTPHeaders::addHeader(CSString *name, CSString *value)
194
new_(iHeaders, CSVector(5));
205
void CSHTTPHeaders::addHeader(const char *name, CSString *value)
212
n = CSString::newString(name);
215
new_(iHeaders, CSVector(5));
225
void CSHTTPHeaders::addHeader(const char *name, uint64_t value)
229
snprintf(buffer, 30, "%"PRIu64, value);
230
addHeader(name, buffer);
233
void CSHTTPHeaders::removeHeader(CSString *name)
240
for (uint32_t i=0; i<iHeaders->size(); ) {
241
h = (CSHeader *) iHeaders->get(i);
242
if (h->getName()->compare(name) == 0) {
253
void CSHTTPHeaders::removeHeader(const char *name)
255
removeHeader(CSString::newString(name));
258
CSString *CSHTTPHeaders::getHeaderValue(const char *name)
266
for (uint32_t i=0; i<iHeaders->size(); i++) {
267
h = (CSHeader *) iHeaders->get(i);
268
if (h->getName()->compare(name) == 0) {
278
const char *CSHTTPHeaders::getHeaderCStringValue(const char *name)
283
for (uint32_t i=0; i<iHeaders->size(); i++) {
284
h = (CSHeader *) iHeaders->get(i);
285
if (h->getName()->compare(name) == 0) {
286
return h->getValue()->getCString();
293
void CSHTTPHeaders::writeHeader(CSOutputStream *out, bool trace)
298
for (uint32_t i=0; i<iHeaders->size(); i++) {
299
h = (CSHeader *) iHeaders->get(i);
300
h->write(out, trace);
305
bool CSHTTPHeaders::keepAlive()
310
bool CSHTTPHeaders::expect100Continue()
312
return iExpect100Continue;
315
bool CSHTTPHeaders::unknownEpectHeader()
317
return iUnknownEpectHeader;
321
* ---------------------------------------------------------------
325
CSHTTPInputStream::CSHTTPInputStream(CSInputStream* in):
336
CSHTTPInputStream::~CSHTTPInputStream()
343
void CSHTTPInputStream::readHead(bool trace)
345
CSStringBuffer *sb = NULL;
346
bool first_line = true;
352
sb = iInput->readLine();
357
CSL.log(self, CSLog::Protocol, "HTTP Request - Header:\n");
358
printf("%s\n", sb->getCString());
360
if (sb->length() == 0) {
368
start = sb->ignore(0, ' ');
369
end = sb->find(start, ' ');
370
str = sb->substr(start, end - start);
371
if (str->startsWith("HTTP")) { // Reply header
375
start = sb->ignore(end, ' ');
376
end = sb->find(start, ' ');
378
CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
380
str = sb->substr(start, end - start);
381
iStatus = atol(str->getCString());
383
start = sb->ignore(end, ' ');
384
end = sb->find(start, '\r');
386
CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
387
iStatusPhrase = sb->substr(start, end - start);
390
iStatusPhrase = NULL;
392
start = sb->ignore(end, ' ');
393
end = sb->find(start, ' ');
395
CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
396
iRequestURI = sb->substr(start, end - start);
397
start = sb->ignore(end, ' ');
398
end = sb->find(start, ' ');
400
CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
401
iHTTPVersion = sb->substr(start, end - start);
406
uint32_t nstart, nend;
407
uint32_t vstart, vend;
409
nstart = sb->ignore(0, ' ');
410
nend = sb->find(nstart, ':');
412
vstart = sb->ignore(nend+1, ' ');
413
vend = sb->find(vstart, '\r');
415
nend = sb->trim(nend, ' ');
416
vend = sb->trim(vend, ' ');
419
CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
420
addHeader(sb->getBuffer(nstart), nend-nstart, sb->getBuffer(vstart), vend-vstart);
428
void CSHTTPInputStream::readBody()
433
if (getContentLength(&body_size)) {
434
iBody.setLength((size_t) body_size);
436
while (len < body_size) {
437
tfer = read(iBody.getBuffer(len), (size_t)(body_size - len));
439
CSException::throwException(CS_CONTEXT, CS_ERR_BODY_INCOMPLETE, "POST data incomplete");
444
CSStringBuffer *sb = NULL;
446
/* Read until we have an empty line. */
451
if (sb->length() == 0) {
455
iBody.append(sb->getBuffer(0), sb->length());
456
iBody.append((char) '\n');
462
bool CSHTTPInputStream::getRange(uint64_t *size, uint64_t *offset)
465
bool haveRange = false;
467
if ((val = getHeaderValue("Range"))) {
468
uint64_t first_byte = 0, last_byte = 0;
469
const char *range = val->getCString();
471
if (range && (val->compare("bytes=", 6) == 0)) {
472
if ((sscanf(range + 6, "%"PRIu64"-%"PRIu64"", &first_byte, &last_byte) == 2) && (last_byte >= first_byte)) {
473
*offset = (uint64_t) first_byte;
474
*size =last_byte - first_byte + 1;
484
bool CSHTTPInputStream::getContentLength(uint64_t *length)
489
if ((val = getHeaderValue("Content-Length"))) {
490
const char *len = val->getCString();
493
sscanf(len, "%"PRIu64"", &size);
501
const char *CSHTTPInputStream::getMethod()
505
return iMethod->getCString();
508
void CSHTTPInputStream::close()
515
size_t CSHTTPInputStream::read(char *b, size_t len)
518
return_(iInput->read(b, len));
521
int CSHTTPInputStream::read()
524
return_(iInput->read());
527
int CSHTTPInputStream::peek()
530
return_(iInput->peek());
533
void CSHTTPInputStream::freeHead()
542
iRequestURI->release();
546
iHTTPVersion->release();
550
iStatusPhrase->release();
551
iStatusPhrase = NULL;
557
CSHTTPInputStream *CSHTTPInputStream::newStream(CSInputStream* i)
559
CSHTTPInputStream *s;
561
if (!(s = new CSHTTPInputStream(i))) {
563
CSException::throwOSError(CS_CONTEXT, ENOMEM);
569
* ---------------------------------------------------------------
570
* HTTP OUTPUT STREAMS
573
CSHTTPOutputStream::CSHTTPOutputStream(CSOutputStream* out):
583
iBody.setGrowSize(120);
586
CSHTTPOutputStream::~CSHTTPOutputStream()
594
void CSHTTPOutputStream::print(const char *str, bool trace)
602
void CSHTTPOutputStream::print(int32_t value, bool trace)
606
iOutput->print(value);
609
void CSHTTPOutputStream::print(uint64_t value, bool trace)
612
printf("%"PRIu64, value);
613
iOutput->print(value);
616
void CSHTTPOutputStream::writeHeaders(bool trace)
618
writeHeader(this, trace);
622
void CSHTTPOutputStream::writeHead(bool trace)
626
CSL.log(self, CSLog::Protocol, "HTTP Reply - Header:\n");
627
print("HTTP/1.1 ", trace);
628
print(iStatus, trace);
630
print(getReasonPhrase(iStatus), trace);
631
print("\r\n", trace);
632
writeHeader(iOutput, trace);
633
print("Content-Length: ", trace);
634
print(iContentLength, trace);
635
print("\r\n", trace);
636
if (iRangeSize && (iStatus == 200)) {
637
print("Content-Range: bytes ", trace);
638
print(iRangeOffset, trace);
640
print(iRangeOffset + iRangeSize -1, trace);
642
print(iTotalLength, trace);
643
print("\r\n", trace);
645
print("\r\n", trace);
650
void CSHTTPOutputStream::clearBody()
659
void CSHTTPOutputStream::writeBody()
661
iOutput->write(iBody.getBuffer(0), iBody.length());
664
void CSHTTPOutputStream::appendBody(const char *str)
667
iContentLength = iBody.length();
670
void CSHTTPOutputStream::appendBody(int32_t value)
673
iContentLength = iBody.length();
676
void CSHTTPOutputStream::appendBody(uint32_t value)
679
iContentLength = iBody.length();
682
void CSHTTPOutputStream::appendBody(uint64_t value)
685
iContentLength = iBody.length();
688
const char *CSHTTPOutputStream::getBodyData()
690
return iBody.getCString();
693
size_t CSHTTPOutputStream::getBodyLength()
695
return iBody.length();
698
void CSHTTPOutputStream::setBody(CSStringBufferImpl *buf)
701
iContentLength = iBody.length();
704
void CSHTTPOutputStream::close()
711
void CSHTTPOutputStream::write(const char *b, size_t len)
714
iOutput->write(b, len);
718
void CSHTTPOutputStream::flush()
725
void CSHTTPOutputStream::write(char b)
732
const char *CSHTTPOutputStream::getReasonPhrase(int code)
734
const char *message = "Unknown Code";
737
case 100: message = "Continue"; break;
738
case 101: message = "Switching Protocols"; break;
739
case 200: message = "OK"; break;
740
case 201: message = "Created"; break;
741
case 202: message = "Accepted"; break;
742
case 203: message = "Non-Authoritative Information"; break;
743
case 204: message = "No Content"; break;
744
case 205: message = "Reset Content"; break;
745
case 206: message = "Partial Content"; break;
746
case 300: message = "Multiple Choices"; break;
747
case 301: message = "Moved Permanently"; break;
748
case 302: message = "Found"; break;
749
case 303: message = "See Other"; break;
750
case 304: message = "Not Modified"; break;
751
case 305: message = "Use Proxy"; break;
752
case 307: message = "Temporary Redirect"; break;
753
case 400: message = "Bad Request"; break;
754
case 401: message = "Unauthorized"; break;
755
case 402: message = "Payment Required"; break;
756
case 403: message = "Forbidden"; break;
757
case 404: message = "Not Found"; break;
758
case 405: message = "Method Not Allowed"; break;
759
case 406: message = "Not Acceptable"; break;
760
case 407: message = "Proxy Authentication Required"; break;
761
case 408: message = "Request Time-out"; break;
762
case 409: message = "Conflict"; break;
763
case 410: message = "Gone"; break;
764
case 411: message = "Length Required"; break;
765
case 412: message = "Precondition Failed"; break;
766
case 413: message = "Request Entity Too Large"; break;
767
case 414: message = "Request-URI Too Large"; break;
768
case 415: message = "Unsupported Media Type"; break;
769
case 416: message = "Requested range not satisfiable"; break;
770
case 417: message = "Expectation Failed"; break;
771
case 500: message = "Internal Server Error"; break;
772
case 501: message = "Not Implemented"; break;
773
case 502: message = "Bad Gateway"; break;
774
case 503: message = "Service Unavailable"; break;
775
case 504: message = "Gateway Time-out"; break;
776
case 505: message = "HTTP Version not supported"; break;
781
CSHTTPOutputStream *CSHTTPOutputStream::newStream(CSOutputStream* i)
783
CSHTTPOutputStream *s;
785
if (!(s = new CSHTTPOutputStream(i))) {
787
CSException::throwOSError(CS_CONTEXT, ENOMEM);