~drizzle-trunk/drizzle/development

1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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
1548.2.2 by Barry.Leslie at PrimeBase
A lot of minor changes to clean up the code and to get it to build with Drizzle.
63
void CSHeader::setName(const char *name, uint32_t len)
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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
1548.2.2 by Barry.Leslie at PrimeBase
A lot of minor changes to clean up the code and to get it to build with Drizzle.
78
void CSHeader::setValue(const char *value, uint32_t len)
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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;
1548.2.2 by Barry.Leslie at PrimeBase
A lot of minor changes to clean up the code and to get it to build with Drizzle.
141
	uint32_t i =0;
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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());
1548.2.19 by Barry.Leslie at PrimeBase
Fixes for longjmp clobber problem, (Hopefully)
156
	push_(h);
157
	h->setName(name);
158
	h->setValue(value);
159
	pop_(h);
160
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
161
	addHeader(h);
162
	exit_();
163
}
164
1548.2.2 by Barry.Leslie at PrimeBase
A lot of minor changes to clean up the code and to get it to build with Drizzle.
165
void CSHTTPHeaders::addHeader(const char *name, uint32_t nlen, const char *value, uint32_t vlen)
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
166
{
167
	CSHeader *h;
168
169
	enter_();
170
	if (!iHeaders)
171
		new_(iHeaders, CSVector(5));
172
173
	new_(h, CSHeader());
1548.2.19 by Barry.Leslie at PrimeBase
Fixes for longjmp clobber problem, (Hopefully)
174
	push_(h);
175
	h->setName(name, nlen);
176
	h->setValue(value, vlen);
177
	pop_(h);
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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
1548.2.2 by Barry.Leslie at PrimeBase
A lot of minor changes to clean up the code and to get it to build with Drizzle.
228
		for (uint32_t i=0; i<iHeaders->size(); ) {
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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
1548.2.2 by Barry.Leslie at PrimeBase
A lot of minor changes to clean up the code and to get it to build with Drizzle.
258
		for (uint32_t i=0; i<iHeaders->size(); i++) {
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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
1548.2.2 by Barry.Leslie at PrimeBase
A lot of minor changes to clean up the code and to get it to build with Drizzle.
276
		for (uint32_t i=0; i<iHeaders->size(); i++) {
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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;
1548.2.2 by Barry.Leslie at PrimeBase
A lot of minor changes to clean up the code and to get it to build with Drizzle.
325
	uint32_t			start, end;
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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
		}
1548.2.19 by Barry.Leslie at PrimeBase
Fixes for longjmp clobber problem, (Hopefully)
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, ' ');
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
352
				end = sb->find(start, ' ');
1548.2.19 by Barry.Leslie at PrimeBase
Fixes for longjmp clobber problem, (Hopefully)
353
				if (start > end)
354
					CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
355
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
356
				str = sb->substr(start, end - start);
1548.2.19 by Barry.Leslie at PrimeBase
Fixes for longjmp clobber problem, (Hopefully)
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);
1548.2.1 by Barry.Leslie at PrimeBase
Added the PBMS daemon plugin.
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