2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
24 #import "NGHttpMessageParser.h"
25 #import "NGHttpMessage.h"
26 #import "NGHttpRequest.h"
27 #import "NGHttpResponse.h"
28 #import "NGHttpHeaderFieldParser.h"
29 #import "NGHttpBodyParser.h"
31 static inline void NGAddChar(NSMutableData *_data, int c) {
33 static Class lastClass = Nil; // THREAD
34 static void (*addBytes)(id,SEL,void*,unsigned) = NULL;
35 if (_data == nil) return;
37 if (*(Class *)_data != lastClass) {
38 lastClass = *(Class *)_data;
39 addBytes = (void*)[_data methodForSelector:@selector(appendBytes:length:)];
43 addBytes(_data, @selector(appendBytes:length:), &c8, 1);
45 [_data appendBytes:&c8 length:1];
48 @implementation NGHttpMessageParser
51 return [super version] + 0 /* v3 */;
54 static NGMimeType *wwwFormUrlEncoded = nil;
55 static NGMimeType *multipartFormData = nil;
56 static id<NGMimeBodyParser> wwwFormUrlParser = nil;
57 static id<NGMimeBodyParser> multipartFormDataParser = nil;
60 static BOOL isInitialized = NO;
64 NSAssert2([super version] == 3,
65 @"invalid superclass (%@) version %i !",
66 NSStringFromClass([self superclass]), [super version]);
69 [[NGMimeType mimeType:@"application/x-www-form-urlencoded"] retain];
70 multipartFormData = [[NGMimeType mimeType:@"multipart/form-data"] retain];
72 wwwFormUrlParser = [[NGFormUrlBodyParser alloc] init];
73 multipartFormDataParser = [[NGHttpMultipartFormDataBodyParser alloc] init];
77 static inline int _readByte(NGHttpMessageParser *self) {
79 ? self->readByte(self->source, @selector(readByte))
80 : [self->source readByte];
83 static inline int _skipLWSP(NGHttpMessageParser *self, int _c) {
85 while ((c == 32) || (c == '\t'))
93 if ((self = [super init])) {
94 self->useContentLength = YES;
100 [self->reason release];
101 [self->methodName release];
103 [self->version release];
109 - (void)setDelegate:(id)_delegate {
110 [super setDelegate:_delegate];
112 self->httpDelegateRespondsTo.httpParserWillParseRequest
113 = [self->delegate respondsToSelector:@selector(httpParserWillParseRequest:)];
114 self->httpDelegateRespondsTo.httpParserWillParseResponse
115 = [self->delegate respondsToSelector:@selector(httpParserWillParseResponse:)];
117 self->httpDelegateRespondsTo.httpParserDidParseRequest
118 = [self->delegate respondsToSelector:@selector(httpParser:didParseRequest:)];
119 self->httpDelegateRespondsTo.httpParserDidParseResponse
120 = [self->delegate respondsToSelector:@selector(httpParser:didParseResponse:)];
125 - (id<NGMimeHeaderFieldParser>)parserForHeaderField:(NSString *)_name {
126 //NSLog(@"asked for header field parser of %@", _name);
128 //#warning does not use header field parsers by default ...
131 return [NGMimeHeaderFieldParserSet defaultHttpHeaderFieldParserSet];
137 - (BOOL)prepareForParsingFromStream:(id<NGStream>)_stream {
138 if ([super prepareForParsingFromStream:_stream]) {
139 if (self->methodName) {
140 [self->methodName release]; self->methodName = nil;
143 [self->uri release]; self->uri = nil;
146 [self->version release]; self->version = nil;
153 - (void)finishParsingOfPart:(id<NGMimePart>)_part {
154 [self->methodName release]; self->methodName = nil;
157 // parse request/response line
159 - (BOOL)parseRequestLine {
163 NSAssert1(self->source, @"missing source %@ !", self);
166 /* ignore prefix CRLF's, as described in RFC (HTTP/1.1, section 4.1) */
170 while ((c == 13) || (c == 10));
171 if (c == -1) /* unexpected EOF */
175 /* parse the request method */
181 buf[i] = c; // one char is already present ..
185 while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1) && (i < 15));
188 if (c == -1) // unexpected EOF
192 NSLog(@"WARNING: truncated request method "
193 @"(may not longer than 15 chars): %s",
197 self->methodName = [[NSString alloc] initWithCString:buf length:i];
200 c = _skipLWSP(self, c);
201 if (c == -1) { // unexpected EOF
202 RELEASE(self->methodName); self->methodName = nil;
206 /* read data till end of line ... */
208 NSMutableData *data = nil;
209 unsigned char *bytes, *tmp;
212 data = [[NSMutableData alloc] initWithCapacity:256];
214 NGAddChar(data, c); /* one char is in queue ... */
217 while ((c != 13) && (c != 10) && (c != -1));
220 if (c == -1) { /* unexpected EOF */
221 [data release]; data = nil;
222 [self->methodName release]; self->methodName = nil;
226 if (c == 13) { /* if CR */
227 c = _readByte(self); /* read LF */
229 if ((c != 10) && (c != -1)) {
230 NSLog(@"WARNING(%s): missed LF after CR (got %i)\n",
231 __PRETTY_FUNCTION__, c);
235 bytes = [data mutableBytes];
237 /* strip trailing spaces ... */
240 if (bytes[len - 1] != 32) break;
245 if ((tmp = rindex(bytes, 32))) {
248 if ((t2 = strstr(tmp, "HTTP"))) {
249 /* has a HTTP version spec ... */
253 self->version = [[NSString alloc] initWithCString:tmp];
255 /* strip trailing spaces ... */
258 if (bytes[len - 1] != 32) break;
264 /* has no HTTP version spec, but possibly trailing spaces */
266 /* strip trailing spaces ... */
269 if (bytes[len - 1] != 32) break;
276 /* has no HTTP version spec */
278 self->uri = [[NSString alloc] initWithCString:bytes];
280 RELEASE(data); data = nil;
284 [self logWithFormat:@"parsed request line: %@ uri=%@",
285 self->methodName, self->uri];
288 /* now start processing request line (one char is already present) .. */
290 { /* process method */
295 buf[i] = c; // one char is already present ..
299 while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1) && (i < 15));
302 if (c == -1) // unexpected EOF
306 NSLog(@"WARNING: truncated request method "
307 @"(may not longer than 15 chars): %s",
311 self->methodName = [[NSString alloc] initWithCString:buf length:i];
314 c = _skipLWSP(self, c);
315 if (c == -1) { // unexpected EOF
316 RELEASE(self->methodName); self->methodName = nil;
321 NSMutableData *data = nil;
323 data = [[NSMutableData allocWithZone:[self zone]] initWithCapacity:256];
328 while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1));
329 if (c == -1) { // unexpected EOF
330 RELEASE(data); data = nil;
331 RELEASE(self->methodName); self->methodName = nil;
335 self->uri = [[NSString allocWithZone:[self zone]]
336 initWithCString:[data bytes] length:[data length]];
337 RELEASE(data); data = nil;
340 c = _skipLWSP(self, c);
341 if (c == -1) { // unexpected EOF
342 RELEASE(self->methodName); self->methodName = nil;
343 RELEASE(self->uri); self->uri = nil;
347 if ((c == 13) || (c == 10)) { // no HTTP version was provided, using HTTP/0.9
348 if (c == 13) // if CR
349 c = _readByte(self); // read LF
352 NSLog(@"WARNING: expected LF after CR in request line, got %i", c);
354 self->version = @"HTTP/0.9";
356 else { /* HTTP version next .. */
361 buf[i] = c; // one char is already present ..
365 while ((c != 13) && (c != 10) && (c != 32) && (c != '\t') &&
366 (c != -1) && (i < 15));
369 if (c == -1) { // unexpected EOF
370 RELEASE(self->methodName); self->methodName = nil;
371 RELEASE(self->uri); self->uri = nil;
376 NSLog(@"WARNING: truncated protocol version "
377 @"(may not be longer than 15 chars): %s", buf);
380 self->version = [[NSString allocWithZone:[self zone]]
381 initWithCString:buf length:i];
383 /* and now read all remaining chars (spaces and CRLF..) */
384 while ((c != 10) && (c != -1))
387 if (c == -1) { // unexpected EOF
388 RELEASE(self->methodName); self->methodName = nil;
389 RELEASE(self->uri); self->uri = nil;
390 RELEASE(self->version); self->version = nil;
398 - (BOOL)parseStatusLine {
402 NSAssert1(self->source, @"missing source %@ !", self);
405 /* ignore prefix CRLF's, as described in RFC (HTTP/1.1, section 4.1) */
409 while ((c == 13) || (c == 10));
410 if (c == -1) // unexpected EOF
413 /* now start processing response line (one char is already present) .. */
415 { /* process HTTP version */
420 buf[i] = c; // one char is already present ..
424 while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1) && (i < 15));
427 if (c == -1) // unexpected EOF
431 NSLog(@"WARNING: truncated response version "
432 @"(may not longer than 15 chars): %s",
436 self->version = [[NSString alloc] initWithCString:buf length:i];
439 c = _skipLWSP(self, c);
440 if (c == -1) { // unexpected EOF
441 RELEASE(self->methodName); self->methodName = nil;
445 { /* process HTTP status */
450 buf[i] = c; // one char is already present ..
454 while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1) && (i < 5));
457 if (c == -1) // unexpected EOF
461 NSLog(@"WARNING: truncated response status "
462 @"(may not longer than 3 chars): %s",
466 self->status = atoi(buf);
469 if ((c = _skipLWSP(self, c)) == -1) { // unexpected EOF
470 RELEASE(self->methodName); self->methodName = nil;
471 RELEASE(self->uri); self->uri = nil;
475 if ((c == 13) || (c == 10)) { // no HTTP reason was provided
476 if (c == 13) // if CR
477 c = _readByte(self); // read LF
480 NSLog(@"WARNING: expected LF after CR in request line, got %i", c);
482 else { // HTTP reason text next
485 // and now read all remaining chars (spaces and CRLF..)
486 while ((c != 10) && (c != -1))
489 if (c == -1) { // unexpected EOF
490 RELEASE(self->reason); self->reason = nil;
491 RELEASE(self->version); self->version = nil;
499 - (BOOL)parseStartLine {
500 return (self->flags.parseRequest)
501 ? [self parseRequestLine]
502 : [self parseStatusLine];
505 - (BOOL)parsePrefix {
506 if ([super parsePrefix])
507 return [self parseStartLine];
512 - (NGMimeBodyParser *)parserForBodyOfPart:(id<NGMimePart>)_part
515 NGMimeType *contentType;
517 contentType = [_part contentType];
520 NSLog(@"%s: was asked for parser for type %@ (data with len %d) ..",
521 __PRETTY_FUNCTION__, contentType, [_dt length]);
524 if ([contentType hasSameType:wwwFormUrlEncoded])
525 return (NGMimeBodyParser *)wwwFormUrlParser;
527 return (NGMimeBodyParser *)[super parserForBodyOfPart:_part data:_dt];
530 - (void)parseBodyOfPart:(id<NGMimePart>)_part {
531 BOOL doParse, hasCLenHeader;
535 NSLog(@"WARNING(%s:%i): got no part !", __PRETTY_FUNCTION__, __LINE__);
539 /* parse only if content-length > 0 */
540 clenValues = [_part valuesOfHeaderFieldWithName:@"content-length"];
541 hasCLenHeader = clenValues ? YES : NO;
542 if ((clenValues = [clenValues nextObject])) {
543 if ([(id)_part contentLength] > 0)
546 //NSLog(@"%s: does not parse body, clen is 0", __PRETTY_FUNCTION__);
551 /* parse until EOF */
553 NSLog(@"WARNING(%s): parsing until EOF, "
554 @"missed content-length header in part %@..",
555 __PRETTY_FUNCTION__, _part);
560 if (self->flags.parseRequest) {
563 rq = (NGHttpRequest *)_part;
565 if (![rq isKindOfClass:[NGHttpRequest class]]) {
566 NSLog(@"ERROR(%s:%i): got invalid part for request parsing !",
567 __PRETTY_FUNCTION__, __LINE__);
571 switch ([rq method]) {
572 case NGHttpMethod_GET:
573 case NGHttpMethod_OPTIONS:
574 case NGHttpMethod_HEAD:
575 case NGHttpMethod_DELETE:
576 case NGHttpMethod_UNLOCK:
577 /* never parse body of the requests above */
578 if ([rq contentLength] > 0) {
580 @"WARNING: expected no content with this method !"];
585 case NGHttpMethod_POST:
586 case NGHttpMethod_PUT:
588 if (doParse && ([rq contentLength] == 0)) {
591 HTTP/1.0, HTTP/0.9 - read till EOF if no content-length is set
592 HTTP/1.1 and above: if no content-length is set, body is empty
594 if ([rq majorVersion] < 1)
596 else if ([rq majorVersion] == 1 && [rq minorVersion] == 0)
605 [super parseBodyOfPart:_part];
609 NSAssert([_part isKindOfClass:[NGHttpResponse class]],
610 @"part should be a response ..");
616 [super parseBodyOfPart:_part];
622 - (NGHttpRequest *)produceRequestWithMethodName:(NSString *)_method
623 uri:(NSString *)_uri version:(NSString *)_version
624 header:(NGHashMap *)_header
626 NGHttpRequest *request = nil;
628 request = [[NGHttpRequest allocWithZone:[self zone]]
629 initWithMethod:_method
633 return AUTORELEASE(request);
636 - (NGHttpResponse *)produceResponseWithStatusCode:(int)_code
637 statusText:(NSString *)_text version:(NSString *)_version
638 header:(NGHashMap *)_header
640 NGHttpResponse *response = nil;
642 response = [[NGHttpResponse allocWithZone:[self zone]]
647 return AUTORELEASE(response);
650 - (id<NGMimePart>)producePartWithHeader:(NGHashMap *)_header {
651 // NSLog(@"producing part with header: %@", _header);
653 if (self->flags.parseRequest) {
654 NGHttpRequest *request = nil;
656 request = [self produceRequestWithMethodName:self->methodName
658 version:self->version
661 RELEASE(self->methodName); self->methodName = nil;
662 RELEASE(self->uri); self->uri = nil;
663 RELEASE(self->version); self->version = nil;
668 NGHttpResponse *response = nil;
670 response = [self produceResponseWithStatusCode:self->status
671 statusText:self->reason
672 version:self->version
675 RELEASE(self->version); self->version = nil;
681 - (NGHttpRequest *)parseRequestFromStream:(id<NGStream>)_stream {
682 NGHttpRequest *request = nil;
685 NSAssert1(_stream, @"missing stream %@ ..", _stream);
688 self->flags.parseRequest = YES;
690 if (self->httpDelegateRespondsTo.httpParserWillParseRequest) {
691 if ([self->delegate httpParserWillParseRequest:self] == NO) {
697 //NSLog(@"%s: parse part from stream ..", __PRETTY_FUNCTION__);
698 request = (NGHttpRequest *)[self parsePartFromStream:_stream];
701 if (self->httpDelegateRespondsTo.httpParserDidParseRequest)
702 [self->delegate httpParser:self didParseRequest:request];
707 - (NGHttpResponse *)parseResponseFromStream:(id<NGStream>)_stream {
708 NGHttpResponse *response = nil;
711 NSAssert1(_stream, @"missing stream %@ ..", _stream);
714 self->flags.parseRequest = NO;
716 if (self->httpDelegateRespondsTo.httpParserWillParseResponse) {
717 if ([self->delegate httpParserWillParseResponse:self] == NO) {
723 response = (NGHttpResponse *)[self parsePartFromStream:_stream];
726 if (self->httpDelegateRespondsTo.httpParserDidParseResponse)
727 [self->delegate httpParser:self didParseResponse:response];
733 - (id<NGMimePart>)parsePartFromStream:(id<NGByteSequenceStream>)_stream {
734 NSLog(@"do not use parsePartFromStream: with NGHttpMessageParser !");
740 @end /* NGHttpMessageParser */