libs-gsweb/GSWeb.framework/GSWDefaultAdaptorThread.m
Manuel Guesdon 77cbf60540 2000-08-14 Manuel Guesdon <mguesdon@orange-concept.com>
* GSWeb.framework/GSWApplication.m: added "exit" in terminate
		generate simple response in -handleException:inContext:
		be sure to have a context in -_handleException:inContext:
		generate simple response in  -_handleException:inContext:
		generate simple response in  -handlePageRestorationErrorInContext:
		be sure to have a context in -_handlePageRestorationErrorInContext:
		generate simple response in  -_handlePageRestorationErrorInContext:
		generate simple response in  -handleSessionCreationErrorInContext:
		generate simple response in  -handleSessionRestorationErrorInContext:
		be sure to have a context in -_handleSessionRestorationErrorInContext:
		generate simple response in  -_handleSessionRestorationErrorInContext:
	* GSWeb.framework/GSWComponentRequestHandler.m: added default response when we catch exceptions,...
		handle more exceptions
	* GSWeb.framework/GSWConstants.h/.m: added GSWHTTPHeader_UserAgent
	* GSWeb.framework/GSWDefaultAdaptor.m: terminate application if it is locked
	* GSWeb.framework/GSWDefaultAdaptorThread.m: Added default response to handle exceptions and various errors
		Correct bug in request data reading (request datas were sometimes not complely readen)
	* GSWeb.framework/GSWFileUpload.m: Added Exception handling
	* GSWeb.framework/GSWRequest.m: add exception handling
	* GSWeb.framework/GSWResponse.h/.m: added  GSWResponse (GSWResponseError)
	* GSWAdaptor/Apache/GNUmakefile: added -DEAPI
	* GSWAdaptor/Apache/mod_gsweb.c: added debug traces
	* GSWAdaptor/common/GSWConfig.c/.h: added debug traces, added init parameter
	* Makefile.postamble: added docs generation
	* Doc/GNUstepWeb-HOWTO moved to howto.gsdoc
	* Doc/gsweb.gsdoc: new file
	* GSWeb.framework/Makefile.postamble: added docs generation
	* GSWExtensions.framework/Makefile.postamble: added docs generation
	* GSWExtensionsGSW.framework/Makefile.postamble: added docs generation


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gsweb/trunk@7244 72102866-910b-0410-8b05-ffd578937521
2000-08-25 10:36:16 +00:00

607 lines
18 KiB
Objective-C

/* GSWDefaultAdaptorThread.m - GSWeb: Class GSWDefaultAdaptorThread
Copyright (C) 1999 Free Software Foundation, Inc.
Written by: Manuel Guesdon <mguesdon@sbuilders.com>
Date: Feb 1999
This file is part of the GNUstep Web Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
static char rcsId[] = "$Id$";
#include <gsweb/GSWeb.framework/GSWeb.h>
#include <unistd.h>
#include "NSNonBlockingFileHandle.h"
//====================================================================
@implementation GSWDefaultAdaptorThread
//--------------------------------------------------------------------
-(id)init
{
if ((self=[super init]))
{
ASSIGN(creationDate,[NSDate date]);
};
return self;
};
//--------------------------------------------------------------------
-(id)initWithApp:(GSWApplication*)application_
withAdaptor:(GSWAdaptor*)adaptor_
withStream:(NSFileHandle*)stream_
{
if ((self=[self init]))
{
application=application_;
adaptor=adaptor_;
ASSIGN(stream,stream_);
keepAlive=NO;
isMultiThread=[adaptor isMultiThreadEnabled];
NSDebugMLLog(@"info",@"isMultiThread=%d",(int)isMultiThread);
};
return self;
};
//--------------------------------------------------------------------
-(void)dealloc
{
GSWLogC("dealloc GSWDefaultAdaptorThread");
DESTROY(stream);
GSWLogC("release dates");
DESTROY(creationDate);
DESTROY(runDate);
DESTROY(dispatchRequestDate);
DESTROY(sendResponseDate);
GSWLogC("release pool");
// DESTROY(pool);
[super dealloc];
GSWLogC("dealloc GSWDefaultAdaptorThread end");
};
//--------------------------------------------------------------------
-(GSWAdaptor*)adaptor
{
return adaptor;
};
//--------------------------------------------------------------------
-(NSAutoreleasePool*)pool
{
return pool;
};
//--------------------------------------------------------------------
-(void)setPool:(NSAutoreleasePool*)pool_
destroyLast:(BOOL)destroy_
{
if (destroy_)
{
DESTROY(pool);
};
pool=pool_;
};
//--------------------------------------------------------------------
-(void)run:(id)void_
{
BOOL _requestOk=NO;
NSMutableDictionary* _threadDictionary=nil;
NSString* _requestLine=nil;
NSDictionary* _headers=nil;
NSData* _data=nil;
ASSIGN(runDate,[NSDate date]);
DESTROY(dispatchRequestDate);
DESTROY(sendResponseDate);
[GSWApplication statusLogWithFormat:@"Thread run START"];
pool=[NSAutoreleasePool new];
[GSWApplication logWithFormat:@"pool allocated!"];
if (isMultiThread)
{
_threadDictionary=GSCurrentThreadDictionary();
[_threadDictionary setObject:self
forKey:GSWThreadKey_DefaultAdaptorThread];
[[NSNotificationCenter defaultCenter] addObserver:[self class]
selector:@selector(threadExited:)
name:NSThreadExiting//NSThreadWillExitNotification
object:[NSThread currentThread]];
/*
[NotificationDispatcher addObserver:[self class]
selector:@selector(threadExited:)
name:NSThreadExiting//NSThreadWillExitNotification
object:[NSThread currentThread]];
*/
};
NSDebugMLLog(@"low",@"application:%@",application);
NS_DURING
{
_requestOk=[self readRequestReturnedRequestLine:&_requestLine
returnedHeaders:&_headers
returnedData:&_data];
}
NS_HANDLER
{
LOGException(@"GSWDefaultAdaptorThread: readRequestFromStream Exception:%@ (%@)",localException,[localException reason]);
}
NS_ENDHANDLER;
if (!_requestOk)
{
//TODO
}
else
{
GSWRequest* request=nil;
GSWResponse* response=nil;
NSDebugMLLog(@"info",@"GSWDefaultAdaptorThread: runWithStream requestLine=%@ _headers=%@ _data=%@",
_requestLine,
_headers,
_data);
NS_DURING
{
request=[self createRequestFromRequestLine:_requestLine
headers:_headers
data:_data];
}
NS_HANDLER
{
LOGException(@"GSWDefaultAdaptorThread: createRequestFromData Exception:%@ (%@)",localException,[localException reason]);
}
NS_ENDHANDLER;
if (request)
{
//call application resourceRequestHandlerKey (retourne wr)
//call requets requestHandlerKey (retorune nil)
NSDebugMLLog(@"info",@"GSWDefaultAdaptorThread: run handleRequest:%@",request);
ASSIGN(dispatchRequestDate,[NSDate date]);
NS_DURING
{
response=[application dispatchRequest:request];
}
NS_HANDLER
{
BOOL _isApplicationRequestHandlingLocked=[application isRequestHandlingLocked];
LOGException(@"GSWDefaultAdaptorThread: dispatchRequest Exception:%@ (%@)%s",
localException,
[localException reason],
_isApplicationRequestHandlingLocked ? " Request Handling Locked !" : "");
}
NS_ENDHANDLER;
if (!response)
{
response=[GSWResponse responseWithMessage:@"Application returned no response"
inContext:nil
forRequest:request];
[response _finalizeInContext:nil]; //DO Call _finalizeInContext: !
};
if (response)
{
RETAIN(response);
ASSIGN(sendResponseDate,[NSDate date]);
NS_DURING
{
[self sendResponse:response];
}
NS_HANDLER
{
LOGException(@"GSWDefaultAdaptorThread: sendResponse Exception:%@ (%@)",localException,[localException reason]);
}
NS_ENDHANDLER;
NSDebugMLLog(@"low",@"application:%@",application);
AUTORELEASE(response);
};
};
};
NSDebugMLog0(@"GSWDefaultAdaptorThread: run end");
NSDebugMLLog(@"low",@"application:%@",application);
LOGObjectFnStop();
[GSWApplication statusLogWithFormat:@"threadWillExit START"];
[application threadWillExit];
[GSWApplication statusLogWithFormat:@"threadWillExit STOP"];
if (isMultiThread)
{
NSAssert([NSThread isMultiThreaded],@"No MultiThread !");
[NSThread exit]; //???
}
else
[self threadExited];
[GSWApplication statusLogWithFormat:@"run STOP"];
};
//--------------------------------------------------------------------
-(void)threadExited
{
// LOGObjectFnStart();
NSDebugMLLog(@"low",@"[_defaultAdaptorThread retainCount=%d",
(int)[self retainCount]);
[adaptor adaptorThreadExited:self];
[self setPool:nil
destroyLast:YES];
// LOGObjectFnStop();
GSWLogC("threadExited");
};
//--------------------------------------------------------------------
+(id)threadExited:(NSNotification*)notif_
{
NSThread* _thread=nil;
NSMutableDictionary* _threadDict=nil;
GSWDefaultAdaptorThread* _adaptorThread=nil;
GSWLogC("Start threadExited:");
NSAssert([NSThread isMultiThreaded],@"No MultiThread !");
NSDebugMLLog(@"low",@"notif_=%@",notif_);
_thread=[notif_ object];
NSDebugMLLog(@"low",@"_thread=%@",_thread);
_threadDict = [_thread threadDictionary];
NSDebugMLLog(@"low",@"_threadDict=%@",_threadDict);
_adaptorThread=[_threadDict objectForKey:GSWThreadKey_DefaultAdaptorThread];
NSDebugMLLog(@"low",@"_adaptorThread=%@",_adaptorThread);
[_threadDict removeObjectForKey:GSWThreadKey_DefaultAdaptorThread];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSThreadExiting//NSThreadWillExitNotification
object:_thread];
/* [NotificationDispatcher removeObserver:self
name:NSThreadExiting//NSThreadWillExitNotification
object:_thread];
*/
[_adaptorThread threadExited];
GSWLogC("Stop threadExited:");
GSWLogC("threadExited really exit");
return nil; //??
};
//--------------------------------------------------------------------
+(NSMutableArray*)completeLinesWithData:(NSMutableData*)data_
returnedConsumedCount:(int*)consumedCount_
returnedHeadersEndFlag:(BOOL*)headersEndFlag_
{
NSMutableArray* _lines=nil;
int _length=0;
LOGClassFnStart();
_length=[data_ length];
if (_length>0)
{
NSRange _range=NSMakeRange(0,0);
int i=0;
char* _dataBytes=(char*)[data_ mutableBytes];
BOOL _endHeaders=NO;
while(!_endHeaders && i<_length)
{
if (_dataBytes[i]=='\n')
{
if (_range.length>0)
{
NSString* tmpString=[[[NSString alloc]initWithData:[data_ subdataWithRange:_range]
encoding:NSASCIIStringEncoding]autorelease];
if (!_lines)
_lines=[NSMutableArray array];
[_lines addObject:tmpString];
}
else // End Header
{
_endHeaders=YES;
};
_range.location=i+1;
_range.length=0;
}
else
_range.length++;
i++;
};
_range.length=_length-_range.location;
if (_range.length>0)
memcpy(_dataBytes,_dataBytes+_range.location,_range.length);
[data_ setLength:_range.length];
if (consumedCount_)
*consumedCount_=_length-_range.length;
if (headersEndFlag_)
*headersEndFlag_=_endHeaders;
};
LOGClassFnStop();
return _lines;
};
//--------------------------------------------------------------------
-(BOOL)readRequestReturnedRequestLine:(NSString**)requestLine_
returnedHeaders:(NSDictionary**)headers_
returnedData:(NSData**)data_
{
BOOL ok=NO;
LOGObjectFnStart();
if (!stream)
{
ExceptionRaise0(@"GSWDefaultAdaptorThread",@"no stream");
}
else
{
#define REQUEST_METHOD__UNKNOWN 0
#define REQUEST_METHOD__GET 1
#define REQUEST_METHOD__POST 2
NSMutableData* _pendingData=nil;
NSDate* maxDate=[NSDate dateWithTimeIntervalSinceNow:360]; //360s
NSData* dataBlock=nil;
int sleepTime=250; //250ms
int readenBytesNb=0;
int headersBytesNb=0;
int dataBytesNb=0;
int dataBlockLength=0;
int contentLength=-1;
int _requestMethod=REQUEST_METHOD__UNKNOWN;
BOOL _isRequestLineSetted=NO;
BOOL _isDataStep=NO;
BOOL _isAllDataReaden=NO;
BOOL _isElapsed=NO;
NSMutableDictionary* _headers=nil;
NSString* _userAgent=nil;
NSString* _remoteAddr=nil;
NSDebugMLog0(@"dataBlock try reading");
do
{
dataBlock=[stream availableDataNonBlocking];
dataBlockLength=[dataBlock length];
if (dataBlockLength>0)
{
readenBytesNb+=dataBlockLength;
if (!_pendingData)
_pendingData=(NSMutableData*)[NSMutableData data];
[_pendingData appendData:dataBlock];
if (_isDataStep)
dataBytesNb=[_pendingData length];
else
{
int _newBytesCount=0;
NSMutableArray* _newLines=[GSWDefaultAdaptorThread completeLinesWithData:_pendingData
returnedConsumedCount:&_newBytesCount
returnedHeadersEndFlag:&_isDataStep];
NSDebugMLLog(@"low",@"newLines=%@ isDataStep=%s newBytesCount=%d",
_newLines,
_isDataStep ? "YES" : "NO",
_newBytesCount);
headersBytesNb+=_newBytesCount;
if (_newLines)
{
int i=0;
for(i=0;i<[_newLines count];i++)
{
NSString* _line=[_newLines objectAtIndex:i];
NSDebugMLLog(@"low",@"Line:%@",_line);
NSAssert([_line length]>0,@"No line length");
if (!_isRequestLineSetted)
{
*requestLine_=_line;
_isRequestLineSetted=YES;
}
else
{
NSString* _key=nil;
NSString* _value=nil;
NSArray* _newValue=nil;
NSArray* _prevValue=nil;
NSRange _keyRange=[_line rangeOfString:@":"];
if (_keyRange.length<=0)
{
_key=_line;
NSDebugMLLog(@"low",@"key:%@",_key);
_value=[NSString string];
}
else
{
_key=[_line substringToIndex:_keyRange.location];
NSDebugMLLog(@"low",@"key:%@",_key);
_key=[[_key stringByTrimmingSpaces] lowercaseString];
if (_keyRange.location+1<[_line length])
{
_value=[_line substringFromIndex:_keyRange.location+1];
_value=[_value stringByTrimmingSpaces];
}
else
_value=[NSString string];
NSDebugMLLog(@"low",@"_value:%@",_value);
};
if ([_key isEqualToString:GSWHTTPHeader_ContentLength])
contentLength=[_value intValue];
else if ([_key isEqualToString:GSWHTTPHeader_Method])
{
if ([_value isEqualToString:GSWHTTPHeader_MethodPost])
_requestMethod=REQUEST_METHOD__POST;
else if ([_value isEqualToString:GSWHTTPHeader_MethodGet])
_requestMethod=REQUEST_METHOD__GET;
else
{
NSAssert1(NO,@"Unknwon method %@",_value);
};
}
else if ([_key isEqualToString:GSWHTTPHeader_UserAgent])
_userAgent=_value;
else if ([_key isEqualToString:GSWHTTPHeader_RemoteAddress])
_remoteAddr=_value;
_prevValue=[_headers objectForKey:_key];
NSDebugMLLog(@"low",@"_prevValue:%@",_prevValue);
if (_prevValue)
_newValue=[_prevValue arrayByAddingObject:_value];
else
_newValue=[NSArray arrayWithObject:_value];
if (!_headers)
_headers=[NSMutableDictionary dictionary];
[_headers setObject:_newValue
forKey:_key];
};
};
};
};
};
dataBytesNb=[_pendingData length];
if (_isDataStep)
{
if (_requestMethod==REQUEST_METHOD__GET)
_isAllDataReaden=YES;
else if (_requestMethod==REQUEST_METHOD__POST)
_isAllDataReaden=(dataBytesNb>=contentLength);
};
if (!_isAllDataReaden)
{
_isElapsed=[[NSDate date]compare:maxDate]==NSOrderedDescending;
if (!_isElapsed)
{
usleep(sleepTime);//Is this the good method ? //TODOV
_isElapsed=[[NSDate date]compare:maxDate]==NSOrderedDescending;
};
};
} while (!_isAllDataReaden && !_isElapsed);
NSDebugMLog(@"GSWDefaultAdaptor: _userAgent=%@ _remoteAddr=%@ _isAllDataReaden=%s _isElapsed=%s readenBytesNb=%d contentLength=%d dataBytesNb=%d headersBytesNb=%d",
_userAgent,
_remoteAddr,
_isAllDataReaden ? "YES" : "NO",
_isElapsed ? "YES" : "NO",
readenBytesNb,
contentLength,
dataBytesNb,
headersBytesNb);
ok=_isAllDataReaden;
if (_isAllDataReaden)
{
*headers_=[[_headers copy] autorelease];
if ([_pendingData length]>0)
*data_=[_pendingData copy];
else
*data_=nil;
}
else
{
*requestLine_=nil;
*headers_=nil;
*data_=nil;
};
}
LOGObjectFnStop();
return ok;
};
//--------------------------------------------------------------------
-(GSWRequest*)createRequestFromRequestLine:(NSString*)requestLine_
headers:(NSDictionary*)headers_
data:(NSData*)data_
{
GSWRequest* _request=nil;
NSArray* _requestLineArray=nil;
LOGObjectFnStart();
NSDebugMLog0(@"GSWDefaultAdaptorThread: createRequestFromData");
_requestLineArray=[requestLine_ componentsSeparatedByString:@" "];
NSDebugMLLog(@"low",@"requestLine:%@",requestLine_);
NSDebugMLLog(@"info",@"_requestLineArray:%@",_requestLineArray);
if ([_requestLineArray count]!=3)
{
ExceptionRaise0(@"GSWDefaultAdaptorThread",@"bad request first line (elements count != 3)");
}
else
{
NSString* method=[_requestLineArray objectAtIndex:0];
NSString* url=[_requestLineArray objectAtIndex:1];
NSArray* http=[[_requestLineArray objectAtIndex:2] componentsSeparatedByString:@"/"];
NSDebugMLLog(@"info",@"method=%@",method);
NSDebugMLLog(@"info",@"url=%@",url);
NSDebugMLLog(@"info",@"http=%@",http);
if ([http count]!=2)
{
ExceptionRaise0(@"GSWDefaultAdaptorThread",@"bad request first line (HTTP)");
}
else
{
NSString* httpVersion=[http objectAtIndex:1];
/* if (isHeaderKeysEqual(method,GSWHTTPHeader_MethodPost))
{
*/
_request=[[[GSWRequest alloc] initWithMethod:method
uri:url
httpVersion:httpVersion
headers:headers_
content:data_
userInfo:nil]
autorelease];
/* };*/
};
};
LOGObjectFnStop();
return _request;
};
//--------------------------------------------------------------------
-(void)sendResponse:(GSWResponse*)response
{
LOGObjectFnStart();
[response willSend];
if (response)
{
int headerN=0;
int headerNForKey=0;
NSMutableData* responseData=[[NSMutableData new]autorelease];
NSArray* headerKeys=[response headerKeys];
NSArray* headersForKey=nil;
NSString* key=nil;
NSString* anHeader=nil;
NSString* head=[NSString stringWithFormat:@"HTTP/%@ %d %@%@\n",
[response httpVersion],
[response status],
GSWHTTPHeader_Response_OK,
GSWHTTPHeader_Response_HeaderLineEnd];
/* NSString* cl=[NSString stringWithFormat:@"%@: %d\n",
GSWHTTPHeader_ContentLength,
[[response content] length]];
*/
NSString* empty=[NSString stringWithString:@"\n"];
NSDebugMLLog(@"low",@"head:%@",head);
[responseData appendData:[head dataUsingEncoding:NSASCIIStringEncoding]];
for(headerN=0;headerN<[headerKeys count];headerN++)
{
key=[headerKeys objectAtIndex:headerN];
headersForKey=[response headersForKey:key];
for(headerNForKey=0;headerNForKey<[headersForKey count];headerNForKey++)
{
anHeader=[NSString stringWithFormat:@"%@: %@\n",
key,
[headersForKey objectAtIndex:headerNForKey]];
[responseData appendData:[anHeader dataUsingEncoding:NSASCIIStringEncoding]];
NSDebugMLLog(@"low",@"anHeader:%@",anHeader);
};
};
// NSDebugMLLog(@"low",@"cl:%@",cl);
NSDebugMLLog(@"low",@"empty:%@",empty);
// [responseData appendData:[cl dataUsingEncoding:NSASCIIStringEncoding]];
[responseData appendData:[empty dataUsingEncoding:NSASCIIStringEncoding]];
[stream writeData:responseData];
if ([[response content] length]>0)
{
[responseData setLength:[[response content] length]];
[responseData setData:[response content]];
NSDebugMLLog(@"low",@"[response content]:%@",[response content]);
NSDebugMLLog(@"low",@"[[response content] length]=%d",[[response content] length]);
NSDebugMLLog(@"low",@"Response content String NSASCIIStringEncoding:%@",[[[NSString alloc] initWithData:[response content]
encoding:NSASCIIStringEncoding]
autorelease]);
NSDebugMLLog(@"low",@"Response content String :%@",[[[NSString alloc] initWithData:[response content]
encoding:NSISOLatin1StringEncoding]
autorelease]);
[stream writeData:responseData];
NSDebugMLog0(@"Response content Written");
};
[stream closeFile];
};
LOGObjectFnStop();
};
@end