]> err.no Git - sope/blob - sope-appserver/NGObjWeb/NGHttp/NGHttpMessageParser.m
added svn:keywords and svn:ignore where appropriate. removed CVS artifacts.
[sope] / sope-appserver / NGObjWeb / NGHttp / NGHttpMessageParser.m
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #import "common.h"
24 #import "NGHttpMessageParser.h"
25 #import "NGHttpMessage.h"
26 #import "NGHttpRequest.h"
27 #import "NGHttpResponse.h"
28 #import "NGHttpHeaderFieldParser.h"
29 #import "NGHttpBodyParser.h"
30
31 static inline void NGAddChar(NSMutableData *_data, int c) {
32   unsigned char c8 = c;
33   static Class lastClass = Nil; // THREAD
34   static void  (*addBytes)(id,SEL,void*,unsigned) = NULL;
35   if (_data == nil) return;
36   
37   if (*(Class *)_data != lastClass) {
38     lastClass  = *(Class *)_data;
39     addBytes = (void*)[_data methodForSelector:@selector(appendBytes:length:)];
40   }
41   
42   if (addBytes)
43     addBytes(_data, @selector(appendBytes:length:), &c8, 1);
44   else
45     [_data appendBytes:&c8 length:1];
46 }
47
48 @implementation NGHttpMessageParser
49
50 + (int)version {
51   return [super version] + 0 /* v3 */;
52 }
53
54 static NGMimeType           *wwwFormUrlEncoded = nil;
55 static NGMimeType           *multipartFormData = nil;
56 static id<NGMimeBodyParser> wwwFormUrlParser   = nil;
57 static id<NGMimeBodyParser> multipartFormDataParser = nil;
58
59 + (void)initialize {
60   static BOOL isInitialized = NO;
61   if (!isInitialized) {
62     isInitialized = YES;
63
64     NSAssert2([super version] == 3,
65               @"invalid superclass (%@) version %i !",
66               NSStringFromClass([self superclass]), [super version]);
67     
68     wwwFormUrlEncoded = 
69       [[NGMimeType mimeType:@"application/x-www-form-urlencoded"] retain];
70     multipartFormData = [[NGMimeType mimeType:@"multipart/form-data"] retain];
71     
72     wwwFormUrlParser = [[NGFormUrlBodyParser alloc] init];
73     multipartFormDataParser = [[NGHttpMultipartFormDataBodyParser alloc] init];
74   }
75 }
76
77 static inline int _readByte(NGHttpMessageParser *self) {
78   return self->readByte
79     ? self->readByte(self->source, @selector(readByte))
80     : [self->source readByte];
81 }
82
83 static inline int _skipLWSP(NGHttpMessageParser *self, int _c) {
84   int c = _c;
85   while ((c == 32) || (c == '\t'))
86     c = _readByte(self);
87   return c;
88 }
89
90 /* init */
91
92 - (id)init {
93   if ((self = [super init])) {
94     self->useContentLength = YES;
95   }
96   return self;
97 }
98
99 - (void)dealloc {
100   [self->reason     release];
101   [self->methodName release];
102   [self->uri     release];
103   [self->version release];
104   [super dealloc];
105 }
106
107 // accessors
108
109 - (void)setDelegate:(id)_delegate {
110   [super setDelegate:_delegate];
111   
112   self->httpDelegateRespondsTo.httpParserWillParseRequest
113     = [self->delegate respondsToSelector:@selector(httpParserWillParseRequest:)];
114   self->httpDelegateRespondsTo.httpParserWillParseResponse
115     = [self->delegate respondsToSelector:@selector(httpParserWillParseResponse:)];
116
117   self->httpDelegateRespondsTo.httpParserDidParseRequest
118     = [self->delegate respondsToSelector:@selector(httpParser:didParseRequest:)];
119   self->httpDelegateRespondsTo.httpParserDidParseResponse
120     = [self->delegate respondsToSelector:@selector(httpParser:didParseResponse:)];
121 }
122
123 /* headers */
124
125 - (id<NGMimeHeaderFieldParser>)parserForHeaderField:(NSString *)_name {
126   //NSLog(@"asked for header field parser of %@", _name);
127 #if 1
128   //#warning does not use header field parsers by default ...
129   return nil;
130 #else
131   return [NGMimeHeaderFieldParserSet defaultHttpHeaderFieldParserSet];
132 #endif
133 }
134
135 // preparation
136
137 - (BOOL)prepareForParsingFromStream:(id<NGStream>)_stream {
138   if ([super prepareForParsingFromStream:_stream]) {
139     if (self->methodName) {
140       [self->methodName release]; self->methodName = nil;
141     }
142     if (self->uri) {
143       [self->uri release]; self->uri = nil;
144     }
145     if (self->version) {
146       [self->version release]; self->version = nil;
147     }
148     
149     return YES;
150   }
151   return NO;
152 }
153 - (void)finishParsingOfPart:(id<NGMimePart>)_part {
154   [self->methodName release]; self->methodName = nil;
155 }
156
157 // parse request/response line
158
159 - (BOOL)parseRequestLine {
160   int c;
161   
162 #if DEBUG
163   NSAssert1(self->source, @"missing source %@ !", self);
164 #endif
165   
166   /* ignore prefix CRLF's, as described in RFC (HTTP/1.1, section 4.1) */
167   do {
168     c = _readByte(self);
169   }
170   while ((c == 13) || (c == 10));
171   if (c == -1) /* unexpected EOF */
172     return NO;
173
174 #if 1
175   /* parse the request method */
176   {
177     char buf[16];
178     int  i = 0;
179     
180     do {
181       buf[i] = c; // one char is already present ..
182       c = _readByte(self);
183       i++;
184     }
185     while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1) && (i < 15));
186     buf[i] = '\0';
187     
188     if (c == -1) // unexpected EOF
189       return NO;
190
191     if (i >= 15) {
192       NSLog(@"WARNING: truncated request method "
193             @"(may not longer than 15 chars): %s",
194             buf);
195     }
196
197     self->methodName = [[NSString alloc] initWithCString:buf length:i];
198   }
199   
200   c = _skipLWSP(self, c);
201   if (c == -1) { // unexpected EOF
202     RELEASE(self->methodName); self->methodName = nil;
203     return NO;
204   }
205   
206   /* read data till end of line ... */
207   {
208     NSMutableData *data = nil;
209     unsigned char *bytes, *tmp;
210     unsigned len;
211     
212     data = [[NSMutableData alloc] initWithCapacity:256];
213     do {
214       NGAddChar(data, c); /* one char is in queue ... */
215       c = _readByte(self);
216     }
217     while ((c != 13) && (c != 10) && (c != -1));
218     NGAddChar(data, 0);
219     
220     if (c == -1) { /* unexpected EOF */
221       [data             release]; data = nil;
222       [self->methodName release]; self->methodName = nil;
223       return NO;
224     }
225     
226     if (c == 13) { /* if CR */
227       c = _readByte(self); /* read LF */
228       
229       if ((c != 10) && (c != -1)) {
230         NSLog(@"WARNING(%s): missed LF after CR (got %i)\n",
231               __PRETTY_FUNCTION__, c);
232       }
233     }
234     
235     bytes = [data mutableBytes];
236     
237     /* strip trailing spaces ... */
238     len = strlen(bytes);
239     while (len > 0) {
240       if (bytes[len - 1] != 32) break;
241       len--;
242       bytes[len] = '\0';
243     }
244     
245     if ((tmp = rindex(bytes, 32))) {
246       unsigned char *t2;
247       
248       if ((t2 = strstr(tmp, "HTTP"))) {
249         /* has a HTTP version spec ... */
250         
251         *tmp = '\0';
252         tmp++;
253         self->version = [[NSString alloc] initWithCString:tmp];
254         
255         /* strip trailing spaces ... */
256         len = strlen(bytes);
257         while (len > 0) {
258           if (bytes[len - 1] != 32) break;
259           len--;
260           bytes[len] = '\0';
261         }
262       }
263       else {
264         /* has no HTTP version spec, but possibly trailing spaces */
265         
266         /* strip trailing spaces ... */
267         len = strlen(bytes);
268         while (len > 0) {
269           if (bytes[len - 1] != 32) break;
270           len--;
271           bytes[len] = '\0';
272         }
273       }
274     }
275     else {
276       /* has no HTTP version spec */
277     }
278     self->uri = [[NSString alloc] initWithCString:bytes];
279     
280     RELEASE(data); data = nil;
281   }
282
283 #if 0
284   [self logWithFormat:@"parsed request line: %@ uri=%@",
285           self->methodName, self->uri];
286 #endif
287 #else
288   /* now start processing request line (one char is already present) .. */
289   
290   { /* process method */
291     char buf[16];
292     int  i = 0;
293
294     do {
295       buf[i] = c; // one char is already present ..
296       c = _readByte(self);
297       i++;
298     }
299     while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1) && (i < 15));
300     buf[i] = '\0';
301     
302     if (c == -1) // unexpected EOF
303       return NO;
304
305     if (i >= 15) {
306       NSLog(@"WARNING: truncated request method "
307             @"(may not longer than 15 chars): %s",
308             buf);
309     }
310
311     self->methodName = [[NSString alloc] initWithCString:buf length:i];
312   }
313   
314   c = _skipLWSP(self, c);
315   if (c == -1) { // unexpected EOF
316     RELEASE(self->methodName); self->methodName = nil;
317     return NO;
318   }
319
320   { /* process path */
321     NSMutableData *data = nil;
322     
323     data = [[NSMutableData allocWithZone:[self zone]] initWithCapacity:256];
324     do {
325       NGAddChar(data, c);
326       c = _readByte(self);
327     }
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;
332       return NO;
333     }
334
335     self->uri = [[NSString allocWithZone:[self zone]]
336                            initWithCString:[data bytes] length:[data length]];
337     RELEASE(data); data = nil;
338   }
339
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;
344     return NO;
345   }
346   
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
350
351     if (c != 10)
352       NSLog(@"WARNING: expected LF after CR in request line, got %i", c);
353
354     self->version = @"HTTP/0.9";
355   }
356   else { /* HTTP version next .. */
357     char buf[16];
358     int  i = 0;
359
360     do {
361       buf[i] = c; // one char is already present ..
362       c = _readByte(self);
363       i++;
364     }
365     while ((c != 13) && (c != 10) && (c != 32) && (c != '\t') &&
366            (c != -1) && (i < 15));
367     buf[i] = '\0';
368     
369     if (c == -1) { // unexpected EOF
370       RELEASE(self->methodName); self->methodName = nil;
371       RELEASE(self->uri);        self->uri        = nil;
372       return NO;
373     }
374
375     if (i >= 15) {
376       NSLog(@"WARNING: truncated protocol version "
377             @"(may not be longer than 15 chars): %s", buf);
378     }
379
380     self->version = [[NSString allocWithZone:[self zone]]
381                                initWithCString:buf length:i];
382
383     /* and now read all remaining chars (spaces and CRLF..) */
384     while ((c != 10) && (c != -1))
385       c = _readByte(self);
386
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;
391       return NO;
392     }
393   }
394 #endif
395   return YES;
396 }
397
398 - (BOOL)parseStatusLine {
399   int c;
400
401 #if DEBUG
402   NSAssert1(self->source, @"missing source %@ !", self);
403 #endif
404
405   /* ignore prefix CRLF's, as described in RFC (HTTP/1.1, section 4.1) */
406   do {
407     c = _readByte(self);
408   }
409   while ((c == 13) || (c == 10));
410   if (c == -1) // unexpected EOF
411     return NO;
412   
413   /* now start processing response line (one char is already present) .. */
414
415   { /* process HTTP version */
416     char buf[16];
417     int  i = 0;
418     
419     do {
420       buf[i] = c; // one char is already present ..
421       c = _readByte(self);
422       i++;
423     }
424     while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1) && (i < 15));
425     buf[i] = '\0';
426     
427     if (c == -1) // unexpected EOF
428       return NO;
429     
430     if (i >= 15) {
431       NSLog(@"WARNING: truncated response version "
432             @"(may not longer than 15 chars): %s",
433             buf);
434     }
435     
436     self->version = [[NSString alloc] initWithCString:buf length:i];
437   }
438
439   c = _skipLWSP(self, c);
440   if (c == -1) { // unexpected EOF
441     RELEASE(self->methodName); self->methodName = nil;
442     return NO;
443   }
444
445   { /* process HTTP status */
446     char buf[5];
447     int  i = 0;
448     
449     do {
450       buf[i] = c; // one char is already present ..
451       c = _readByte(self);
452       i++;
453     }
454     while ((c != 32) && (c != '\r') && (c != '\n') && (c != -1) && (i < 5));
455     buf[i] = '\0';
456     
457     if (c == -1) // unexpected EOF
458       return NO;
459     
460     if (i >= 5) {
461       NSLog(@"WARNING: truncated response status "
462             @"(may not longer than 3 chars): %s",
463             buf);
464     }
465
466     self->status = atoi(buf);
467   }
468   
469   if ((c = _skipLWSP(self, c)) == -1) { // unexpected EOF
470     RELEASE(self->methodName); self->methodName = nil;
471     RELEASE(self->uri);        self->uri        = nil;
472     return NO;
473   }
474   
475   if ((c == 13) || (c == 10)) { // no HTTP reason was provided
476     if (c == 13) // if CR
477       c = _readByte(self); // read LF
478
479     if (c != 10)
480       NSLog(@"WARNING: expected LF after CR in request line, got %i", c);
481   }
482   else { // HTTP reason text next
483     // to be done ..
484     
485     // and now read all remaining chars (spaces and CRLF..)
486     while ((c != 10) && (c != -1))
487       c = _readByte(self);
488
489     if (c == -1) { // unexpected EOF
490       RELEASE(self->reason);  self->reason  = nil;
491       RELEASE(self->version); self->version = nil;
492       return NO;
493     }
494   }
495   
496   return YES;
497 }
498
499 - (BOOL)parseStartLine {
500   return (self->flags.parseRequest)
501     ? [self parseRequestLine]
502     : [self parseStatusLine];
503 }
504
505 - (BOOL)parsePrefix {
506   if ([super parsePrefix])
507     return [self parseStartLine];
508   else
509     return NO;
510 }
511
512 - (NGMimeBodyParser *)parserForBodyOfPart:(id<NGMimePart>)_part
513   data:(NSData *)_dt
514 {
515   NGMimeType *contentType;
516   
517   contentType = [_part contentType];
518   
519 #if 0
520   NSLog(@"%s: was asked for parser for type %@ (data with len %d) ..",
521         __PRETTY_FUNCTION__, contentType, [_dt length]);
522 #endif
523   
524   if ([contentType hasSameType:wwwFormUrlEncoded])
525     return (NGMimeBodyParser *)wwwFormUrlParser;
526   
527   return (NGMimeBodyParser *)[super parserForBodyOfPart:_part data:_dt];
528 }
529
530 - (void)parseBodyOfPart:(id<NGMimePart>)_part {
531   BOOL doParse, hasCLenHeader;
532   id   clenValues;
533   
534   if (_part == nil) {
535     NSLog(@"WARNING(%s:%i): got no part !", __PRETTY_FUNCTION__, __LINE__);
536     return;
537   }
538   
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)
544       doParse = YES;
545     else {
546       //NSLog(@"%s: does not parse body, clen is 0", __PRETTY_FUNCTION__);
547       doParse = NO;
548     }
549   }
550   else {
551     /* parse until EOF */
552 #if 0
553     NSLog(@"WARNING(%s): parsing until EOF, "
554           @"missed content-length header in part %@..",
555           __PRETTY_FUNCTION__, _part);
556 #endif
557     doParse = YES;
558   }
559   
560   if (self->flags.parseRequest) {
561     NGHttpRequest *rq;
562     
563     rq = (NGHttpRequest *)_part;
564 #if DEBUG
565     if (![rq isKindOfClass:[NGHttpRequest class]]) {
566       NSLog(@"ERROR(%s:%i): got invalid part for request parsing !",
567             __PRETTY_FUNCTION__, __LINE__);
568     }
569 #endif
570     
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) {
579           [self logWithFormat:
580                   @"WARNING: expected no content with this method !"];
581         }
582         doParse = NO;
583         break;
584         
585       case NGHttpMethod_POST:
586       case NGHttpMethod_PUT:
587       default:
588         if (doParse && ([rq contentLength] == 0)) {
589           /*
590             Two cases: 
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
593           */
594           if ([rq majorVersion] < 1)
595             doParse = YES;
596           else if ([rq majorVersion] == 1 && [rq minorVersion] == 0)
597             doParse = YES;
598           else
599             doParse = NO;
600         }
601         break;
602     }
603     
604     if (doParse)
605       [super parseBodyOfPart:_part];
606   }
607   else {
608 #if DEBUG
609     NSAssert([_part isKindOfClass:[NGHttpResponse class]],
610              @"part should be a response ..");
611 #endif
612     
613     doParse = YES;
614     
615     if (doParse)
616       [super parseBodyOfPart:_part];
617   }
618 }
619
620 /* part parsing */
621
622 - (NGHttpRequest *)produceRequestWithMethodName:(NSString *)_method
623   uri:(NSString *)_uri version:(NSString *)_version
624   header:(NGHashMap *)_header
625 {
626   NGHttpRequest *request = nil;
627
628   request = [[NGHttpRequest allocWithZone:[self zone]]
629                             initWithMethod:_method
630                             uri:_uri
631                             header:_header
632                             version:_version];
633   return AUTORELEASE(request);
634 }
635
636 - (NGHttpResponse *)produceResponseWithStatusCode:(int)_code
637   statusText:(NSString *)_text version:(NSString *)_version
638   header:(NGHashMap *)_header
639 {
640   NGHttpResponse *response = nil;
641   
642   response = [[NGHttpResponse allocWithZone:[self zone]]
643                               initWithStatus:_code
644                               reason:_text
645                               header:_header
646                               version:_version];
647   return AUTORELEASE(response);
648 }
649
650 - (id<NGMimePart>)producePartWithHeader:(NGHashMap *)_header {
651   // NSLog(@"producing part with header: %@", _header);
652   
653   if (self->flags.parseRequest) {
654     NGHttpRequest *request = nil;
655
656     request = [self produceRequestWithMethodName:self->methodName
657                     uri:self->uri
658                     version:self->version
659                     header:_header];
660     
661     RELEASE(self->methodName); self->methodName = nil;
662     RELEASE(self->uri);        self->uri        = nil;
663     RELEASE(self->version);    self->version    = nil;
664
665     return request;
666   }
667   else {
668     NGHttpResponse *response = nil;
669
670     response = [self produceResponseWithStatusCode:self->status
671                      statusText:self->reason
672                      version:self->version
673                      header:_header];
674     
675     RELEASE(self->version); self->version = nil;
676     
677     return response;
678   }
679 }
680
681 - (NGHttpRequest *)parseRequestFromStream:(id<NGStream>)_stream {
682   NGHttpRequest *request = nil;
683
684 #if DEBUG
685   NSAssert1(_stream, @"missing stream %@ ..", _stream);
686 #endif
687   
688   self->flags.parseRequest = YES;
689
690   if (self->httpDelegateRespondsTo.httpParserWillParseRequest) {
691     if ([self->delegate httpParserWillParseRequest:self] == NO) {
692       // parsing aborted
693       return nil;
694     }
695   }
696
697   //NSLog(@"%s: parse part from stream ..", __PRETTY_FUNCTION__);
698   request = (NGHttpRequest *)[self parsePartFromStream:_stream];
699
700   if (request) {
701     if (self->httpDelegateRespondsTo.httpParserDidParseRequest)
702       [self->delegate httpParser:self didParseRequest:request];
703   }
704   return request;
705 }
706
707 - (NGHttpResponse *)parseResponseFromStream:(id<NGStream>)_stream {
708   NGHttpResponse *response = nil;
709   
710 #if DEBUG
711   NSAssert1(_stream, @"missing stream %@ ..", _stream);
712 #endif
713   
714   self->flags.parseRequest = NO;
715
716   if (self->httpDelegateRespondsTo.httpParserWillParseResponse) {
717     if ([self->delegate httpParserWillParseResponse:self] == NO) {
718       // parsing aborted
719       return nil;
720     }
721   }
722   
723   response = (NGHttpResponse *)[self parsePartFromStream:_stream];
724
725   if (response) {
726     if (self->httpDelegateRespondsTo.httpParserDidParseResponse)
727       [self->delegate httpParser:self didParseResponse:response];
728   }
729   return response;
730 }
731
732 #if 0
733 - (id<NGMimePart>)parsePartFromStream:(id<NGByteSequenceStream>)_stream {
734   NSLog(@"do not use parsePartFromStream: with NGHttpMessageParser !");
735   abort();
736   return nil;
737 }
738 #endif
739
740 @end /* NGHttpMessageParser */