2
Copyright (C) 2000-2005 SKYRIX Software AG
4
This file is part of SOPE.
6
SOPE 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
SOPE 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 SOPE; see the file COPYING. If not, write to the
18
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22
#include "WORequestParser.h"
23
#include <NGStreams/NGBufferedStream.h>
24
#include <NGObjWeb/WORequest.h>
27
@implementation WORequestParser
29
- (id)initWithBufferedStream:(NGBufferedStream *)_in {
35
self->in = [_in retain];
36
self->readByte = (void *)[self->in methodForSelector:@selector(readByte)];
41
[self->lastException release];
48
- (void)takeLastException {
49
ASSIGN(self->lastException, [self->in lastException]);
52
static inline int nextChar(WORequestParser *self) {
54
if (self->pushBack != 0) {
59
if ((c = self->readByte(self->in, @selector(readByte))) < 0)
60
[self takeLastException];
64
static inline int nextCharAfterSpaces(WORequestParser *self) {
67
if (self->pushBack != 0) {
68
if (self->pushBack == ' ' || self->pushBack == '\t')
78
c = self->readByte(self->in, @selector(readByte));
80
[self takeLastException];
84
while ((c == ' ') || (c == '\t'));
87
static inline BOOL skipSpaces(WORequestParser *self) {
90
if ((c = nextCharAfterSpaces(self)) > 0)
100
if (c < 0) return NO;
101
if (c == '\n') return YES;
102
if (c != '\r') return NO;
105
if (c < 0) return NO;
106
if (c == '\n') return YES;
112
- (NSString *)parseMethod {
118
for (c = nextChar(self); isalpha(c) && (count < 30); c = nextChar(self)) {
125
/* method name too long */
126
[self logWithFormat:@"method name got too long"];
129
else if (count == 0) {
130
/* method name too short */
131
[self logWithFormat:@"method name got too short"];
135
return [NSString stringWithCString:m length:count];
138
- (NSString *)parseURI {
144
if ((c = nextCharAfterSpaces(self)) < 0)
147
uri = calloc(4096, sizeof(unsigned char));
149
for (count = 0; count < 4001 && (c > 0); count++) {
150
if (c == ' ' || c == '\t') break;
151
if (c == '\r' || c == '\n') break;
157
if (c < 0) return nil;
159
[self logWithFormat:@"uri got too long (max 4000 chars)"];
163
/* feed last char to next parsing step */
166
s = [NSString stringWithCString:uri length:count];
171
- (NSString *)parseVersion {
176
c = nextCharAfterSpaces(self);
177
if (c == '\r' || c == '\n') {
178
/* no version specified */
184
for (; isprint(c) && (count < 15); c = nextChar(self)) {
191
/* version too long */
192
[self logWithFormat:@"http version got too long"];
195
else if (count == 0) {
196
/* version too short, guessing HTTP/0.9 */
200
return [NSString stringWithCString:m length:count];
205
- (NSDictionary *)parseHeaders {
211
- (unsigned)ramDataSizeLimitation {
212
// 64KB TODO: make default
215
- (unsigned)spoolDataSizeLimitation {
216
// 64MB TODO: make default
217
return (64 * 1024 * 1064);
220
- (NSData *)readContentUntilEOF {
225
- (NSData *)readContentOfLength:(unsigned int)_count {
227
static NSData *emptyData = nil;
228
if (emptyData == nil) emptyData = [[NSData alloc] init];
232
if (_count <= [self ramDataSizeLimitation])
233
return [self->in safeReadDataOfLength:_count];
241
- (BOOL)isContentLessMethod:(NSString *)_method {
242
static NSMutableSet *methods = nil;
243
if (methods == nil) {
244
methods = [[NSMutableSet alloc] initWithObjects:nil];
246
return [methods containsObject:_method];
249
- (WORequest *)parseNextRequest {
250
NSString *method, *uri, *v;
251
NSDictionary *headers;
255
ASSIGN(self->lastException, (id)nil);
259
if ((method = [self parseMethod]) == nil)
261
if ((uri = [self parseURI]) == nil)
263
if ((v = [self parseVersion]) == nil)
266
if (![self readCRLF])
269
[self debugWithFormat:@"stage 1: method=%@ uri=%@ version=%@",
274
if ((headers = [self parseHeaders]) == nil)
279
if (![self isContentLessMethod:method]) {
282
if ((clen = [[headers objectForKey:@"content-length"] intValue])) {
283
content = [self readContentOfLength:clen];
288
HTTP/1.0, HTTP/0.9 - read till EOF if no content-length is set
289
HTTP/1.1 and above: if no content-length is set, body is empty
292
if ([v hasPrefix:@"HTTP/0"])
293
content = [self readContentUntilEOF];
294
else if ([v hasPrefix:@"HTTP/1.0"])
295
content = [self readContentUntilEOF];
305
result = [[WORequest alloc] initWithMethod:method uri:uri httpVersion:v
306
headers:headers content:content
308
return [result autorelease];
311
- (NSException *)lastException {
312
return self->lastException;
317
- (NSString *)loggingPrefix {
318
return @"[http-parser]";
320
- (BOOL)isDebuggingEnabled {
321
static int debugOn = -1;
323
debugOn = [[NSUserDefaults standardUserDefaults]
324
boolForKey:@"WORequestParserDebugEnabled"] ? 1 : 0;
326
return debugOn ? YES : NO;
329
@end /* WORequestParser */