* rewrote large parts of GSWDefaultAdaptor

* new GSWHTTPIO and GSWWorkerThread
* dropped GSWDefaultAdaptorThread
* some takeValue:forKey: fixes
* started to remove global inclues


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gsweb/trunk@25611 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Dave Wetzel 2007-11-25 16:49:37 +00:00
parent 250ed2c524
commit e8b2f11ba2
25 changed files with 1113 additions and 1737 deletions

View file

@ -1,6 +1,12 @@
2007-11-25 David Wetzel <dave@turbocat.de>
* GSWAdaptors/Apache2/mod_gsw.c
Works also if a header or empty line at the end of the headers has a \r\n ending.
GSWeb.framework:
* rewrote large parts of GSWDefaultAdaptor
* new GSWHTTPIO and GSWWorkerThread
* dropped GSWDefaultAdaptorThread
* some takeValue:forKey: fixes
* started to remove global inclues
2007-11-24 David Ayers <ayers@fsfe.org>

View file

@ -84,6 +84,7 @@ GSWConstants.m \
GSWTemporaryElement.m \
GSWBaseParser.m \
GSWHTMLRawParser.m \
GSWHTTPIO.m \
GSWDeclaration.m \
GSWDeclarationParser.m \
GSWTemplateParser.m \
@ -164,7 +165,6 @@ GSWComponentRequestHandler.m \
GSWResourceRequestHandler.m \
GSWActionRequestHandler.m \
GSWDirectActionRequestHandler.m \
GSWDefaultAdaptorThread.m \
GSWKeyValueAssociation.m \
GSWHTMLStaticElement.m \
GSWHTMLStaticGroup.m \
@ -191,6 +191,7 @@ GSWStaticResourceRequestHandler.m \
GSWRecording.m \
GSWInputStreamData.m \
GSWActionURL.m \
GSWWorkerThread.m \
#For next Version:
#GSWAdminAction.m \
@ -229,7 +230,6 @@ GSWConstantValueAssociation.h \
GSWContext.h \
GSWCookie.h \
GSWDefaultAdaptor.h \
GSWDefaultAdaptorThread.h \
GSWDeployedBundle.h \
GSWAction.h \
GSWDirectAction.h \

View file

@ -26,6 +26,8 @@
#ifndef _GSWAdaptor_h__
#define _GSWAdaptor_h__
#include <Foundation/NSObject.h>
//====================================================================
// GSWAdaptor
@ -35,13 +37,13 @@
arguments:(NSDictionary*)arguments;
-(void)registerForEvents;
-(void)unregisterForEvents;
-(void)runOnce;
-(BOOL)doesBusyRunOnce;
-(BOOL)dispatchesRequestsConcurrently;
-(int)port;
-(id)workerThreadCount;
-(void)adaptorThreadExited:(GSWDefaultAdaptorThread*)adaptorThread;
-(BOOL)isMultiThreadEnabled;
// deprecated since?
-(void)runOnce;
-(BOOL)doesBusyRunOnce;
@end
//====================================================================
@ -50,4 +52,9 @@
forApplicationNamed:(NSString*)applicationName;
@end
// FIXME: check if that exists:
// -(id)workerThreadCount;
//-(void)adaptorThreadExited:(GSWDefaultAdaptorThread*)adaptorThread;
//-(BOOL)isMultiThreadEnabled;
#endif //_GSWAdaptor_h__

View file

@ -43,11 +43,9 @@ RCS_ID("$Id$")
-(id)initWithName:(NSString*)name
arguments:(NSDictionary*)arguments
{
NSDebugMLog(@"Init");
self=[super init];
LOGObjectFnStop();
return self;
};
}
//--------------------------------------------------------------------
@ -67,54 +65,31 @@ RCS_ID("$Id$")
};
//--------------------------------------------------------------------
// runOnce
// depricated at least in 5.something
-(void)runOnce
{
//TODOFN
};
//Does Nothing
}
//--------------------------------------------------------------------
// doesBusyRunOnce
// depricated at least in 5.something
-(BOOL)doesBusyRunOnce
{
LOGObjectFnNotImplemented(); //TODOFN
return NO;
};
return YES;
}
//--------------------------------------------------------------------
-(BOOL)dispatchesRequestsConcurrently
{
LOGObjectFnNotImplemented(); //TODOFN
return NO;
};
}
//--------------------------------------------------------------------
// port
-(int)port
{
LOGObjectFnNotImplemented(); //TODOFN
return 0;
};
//--------------------------------------------------------------------
-(id)workerThreadCount
{
[self subclassResponsibility: _cmd];
return nil;
};
//--------------------------------------------------------------------
-(void)adaptorThreadExited:(GSWDefaultAdaptorThread*)adaptorThread
{
[self subclassResponsibility: _cmd];
};
//--------------------------------------------------------------------
-(BOOL)isMultiThreadEnabled
{
[self subclassResponsibility: _cmd];
return NO;
};
return -1;
}
@end
@ -126,5 +101,5 @@ RCS_ID("$Id$")
forApplicationNamed:(NSString*)applicationName
{
LOGObjectFnNotImplemented(); //TODOFN
};
}
@end

View file

@ -55,6 +55,23 @@ GSWEB_EXPORT int GSWebNamingConv;//GSWNAMES_INDEX or WONAMES_INDEX
(GSWebNamingConv==GSWNAMES_INDEX ? WONAMES_INDEX : GSWNAMES_INDEX))
GSWEB_EXPORT BOOL WOStrictFlag;
@class GSWSessionStore;
@class GSWStatisticsStore;
@class GSWResourceManager;
@class GSWRequestHandler;
@class GSWLifebeatThread;
@class GSWSession;
@class GSWAdaptor;
@class GSWComponent;
@class GSWElement;
@class GSWResponse;
@class GSWAssociation;
@class GSWComponentDefinition;
@class GSWMultiKeyDictionary;
@class GSWActionRequestHandler;
@class GSWAction;
//====================================================================
@interface GSWApplication : NSObject <NSLocking>
{

View file

@ -1369,6 +1369,7 @@ int GSWApplicationMain(NSString* applicationClassName,
};
//--------------------------------------------------------------------
// dw: I do not know if this exists in WO
-(Class)requestClass
{
NSString* requestClassName=[self requestClassName];
@ -1378,6 +1379,8 @@ int GSWApplicationMain(NSString* applicationClassName,
};
//--------------------------------------------------------------------
// dw: I do not know if this exists in WO
-(GSWRequest*)createRequestWithMethod:(NSString*)aMethod
uri:(NSString*)anURL
httpVersion:(NSString*)aVersion
@ -2507,8 +2510,6 @@ to another instance **/
sessionID=[requestHandlerValues objectForKey:GSWKey_SessionID[GSWebNamingConv]];
if (!sessionID)
{
NSLog(@"Application : sessionID is nil");
if ([self isRefusingNewSessions])
{
NSLog(@"refuseRequest !");

View file

@ -849,7 +849,7 @@ static Class NSStringClass = Nil;
{
// no exception, set the value
[tmpObject takeValue:value
[tmpObject setValue:value
forKey:tmpKey];
}
}
@ -940,7 +940,7 @@ static Class NSStringClass = Nil;
else
{
GSWLogAssertGood(tmpObject);
[tmpObject takeValue:value
[tmpObject setValue:value
forKey:part];
#ifdef HAVE_GDL2
// Turbocat

View file

@ -451,7 +451,7 @@ static Class GSWHTMLBareStringClass = Nil;
[self smartTakeValue: obj
forKey: myKey];
#else
[self takeValue: obj
[self setValue: obj
forKey: myKey];
#endif
}

View file

@ -27,9 +27,19 @@
#ifndef _GSWDefaultAdaptor_h__
#define _GSWDefaultAdaptor_h__
#include "GSWDefines.h"
#include "GSWWOCompatibility.h"
#include "GSWAdaptor.h"
@class GSWWorkerThread;
@class NSDictionary;
@class NSString;
@class NSFileHandle;
@class NSMutableArray;
@class NSLock;
GSWEB_EXPORT int iBlock;
//====================================================================
// GSWDefaultAdaptor
@interface GSWDefaultAdaptor: GSWAdaptor
{
@ -41,12 +51,20 @@ GSWEB_EXPORT int iBlock;
int _workerThreadCount;
int _workerThreadCountMin;
int _workerThreadCountMax;
int _listenQueueSize;
int _maxSocketIdleTime;
int _maxWorkerThreads;
int __nmbOfWorkerThreads;
int __nmbOfActiveThreads;
int __windowSize;
BOOL _isMultiThreadEnabled;
NSFileHandle* _fileHandle;
NSMutableArray* _waitingThreads;
NSMutableArray* _threads;
NSLock* _selfLock;
BOOL _blocked;
BOOL _shouldGrow;
}
-(id)initWithName:(NSString*)name
@ -60,8 +78,6 @@ GSWEB_EXPORT int iBlock;
-(BOOL)dispatchesRequestsConcurrently;
-(int)port;
-(NSString*)host;
-(void)adaptorThreadExited:(GSWDefaultAdaptorThread*)adaptorThread;
-(BOOL)tryLock;
-(void)unlock;
-(void)setWorkerThreadCount:(id)workerThreadCount;
@ -75,6 +91,8 @@ GSWEB_EXPORT int iBlock;
-(BOOL)isConnectionAllowedWithHandle:(NSFileHandle*)handle
returnedMessage:(NSString**)retMessage;
- (void) workerThreadWillExit:(GSWWorkerThread*) thread;
@end
//====================================================================

View file

@ -30,17 +30,26 @@
</license>
**/
#include "config.h"
RCS_ID("$Id$")
#include "config.h"
#include "GSWDefaultAdaptor.h"
#include <Foundation/NSFileHandle.h>
#include <Foundation/NSLock.h>
#include "GSWeb.h"
#include "GSWWorkerThread.h"
#if HAVE_LIBWRAP
#include <tcpd.h>
#include <syslog.h>
#endif
RCS_ID("$Id$")
#if HAVE_LIBWRAP
int deny_severity = LOG_WARNING;
int allow_severity = LOG_INFO;
@ -50,15 +59,30 @@ int allow_severity = LOG_INFO;
#endif
static GSWResponse * static_lastDitchErrorResponse = nil;
//====================================================================
@implementation GSWDefaultAdaptor
+ (void) initialize
{
if (self == [GSWDefaultAdaptor class]) {
if (static_lastDitchErrorResponse == nil) {
static_lastDitchErrorResponse = [GSWResponse new];
[static_lastDitchErrorResponse setStatus:500];
[static_lastDitchErrorResponse appendContentString:@"An Internal Server Error Has Occurred."];
}
}
}
+(GSWResponse*) _lastDitchErrorResponse
{
return static_lastDitchErrorResponse;
}
-(id)initWithName:(NSString*)name
arguments:(NSDictionary*)arguments
{
NSDebugMLog(@"Init");
if ((self=[super initWithName:name
arguments:arguments]))
{
@ -75,92 +99,129 @@ int allow_severity = LOG_INFO;
_workerThreadCountMax=[[arguments objectForKey:GSWOPT_WorkerThreadCountMax[GSWebNamingConv]] intValue];
_isMultiThreadEnabled=[[arguments objectForKey:GSWOPT_MultiThreadEnabled] boolValue];
ASSIGN(_adaptorHost,[arguments objectForKey:GSWOPT_AdaptorHost[GSWebNamingConv]]);
};
LOGObjectFnStop();
if ((_workerThreadCountMax <1) || (_isMultiThreadEnabled == NO)) {
_workerThreadCountMax = 1;
_isMultiThreadEnabled = NO;
}
}
return self;
};
}
//--------------------------------------------------------------------
-(void)dealloc
{
GSWLogMemC("Dealloc GSWDefaultAdaptor");
//TODO? DESTROY(listenPortObject);
GSWLogMemC("Dealloc GSWDefaultAdaptor: host");
DESTROY(_host);
GSWLogMemC("Dealloc GSWDefaultAdaptor: adaptorHost");
DESTROY(_adaptorHost);
GSWLogMemC("Dealloc GSWDefaultAdaptor: fileHandle");
DESTROY(_fileHandle);
GSWLogMemC("Dealloc GSWDefaultAdaptor: threads");
DESTROY(_threads);
GSWLogMemC("Dealloc GSWDefaultAdaptor: waitingThreads");
DESTROY(_waitingThreads);
GSWLogMemC("Dealloc GSWDefaultAdaptor: selfLock");
DESTROY(_selfLock);
GSWLogMemC("Dealloc GSWDefaultAdaptor Super");
[super dealloc];
GSWLogMemC("End Dealloc GSWDefaultAdaptor");
};
}
//--------------------------------------------------------------------
-(void)registerForEvents
{
NSDebugDeepMLog(@"START registerForEvents - %@",
GSCurrentThread());
NSAssert(!_fileHandle,@"fileHandle already exists");
NSDebugDeepMLLog(@"info",@"registerForEvents port=%d",_port);
NSDebugDeepMLLog(@"info",@"registerForEvents host=%@",_host);
if (!_host)
{
ASSIGN(_host,[[NSHost currentHost] name]);
};
_fileHandle=[[NSFileHandle fileHandleAsServerAtAddress:_host
service:GSWIntToNSString(_port)
protocol:@"tcp"] retain];
NSDebugDeepMLLog(@"info",@"fileHandle=%p",(void*)_fileHandle);
NSAssert(_fileHandle,@"No fileHandle to wait for connections");
[[NSNotificationCenter defaultCenter] addObserver:self
selector: @selector(announceNewConnection:)
name: NSFileHandleConnectionAcceptedNotification
object:_fileHandle];
/* [NotificationDispatcher addObserver:self
selector: @selector(announceNewConnection:)
name: NSFileHandleConnectionAcceptedNotification
object:fileHandle];
*/
[_fileHandle acceptConnectionInBackgroundAndNotify];
#ifndef __APPLE__
NSAssert([_fileHandle readInProgress],@"No [_fileHandle readInProgress]");
NSDebugDeepMLog(@"%@ - B readInProgress=%d",
GSCurrentThread(),(int)[_fileHandle readInProgress]);
#endif
[GSWApplication statusLogWithFormat:
@"Thread %@: Waiting for connections on %@:%d.",
GSCurrentThread(),
_host,
_port];
NSDebugDeepMLog(@"STOP registerForEvents");
};
SYNCHRONIZED(_selfLock) {
NSAssert(!_fileHandle,@"fileHandle already exists");
if (!_host) {
ASSIGN(_host, @"localhost");
}
_fileHandle=[[NSFileHandle fileHandleAsServerAtAddress:_host
service:GSWIntToNSString(_port)
protocol:@"tcp"] retain];
NSAssert(_fileHandle,@"No fileHandle to wait for connections");
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(announceNewConnection:)
name:NSFileHandleConnectionAcceptedNotification
object:_fileHandle];
[_fileHandle acceptConnectionInBackgroundAndNotify];
#ifndef __APPLE__
NSAssert([_fileHandle readInProgress],@"No [_fileHandle readInProgress]");
NSDebugDeepMLog(@"%@ - B readInProgress=%d", GSCurrentThread(),(int)[_fileHandle readInProgress]);
#endif
[GSWApplication statusLogWithFormat:
@"Thread %@: Waiting for connections on %@:%d.",
GSCurrentThread(),
_host,
_port];
}
END_SYNCHRONIZED;
}
//PRIVATE use only if locked!
void _workOnHandle(NSFileHandle* handle, id adaptor, NSMutableArray* threadArray, BOOL isMultiThreadEnabled)
{
GSWWorkerThread * thread = [GSWWorkerThread alloc];
thread = [thread initWithApp:GSWApp
adaptor:adaptor
stream:handle];
if (isMultiThreadEnabled) {
[threadArray addObject: thread];
}
[thread release];
}
//PRIVATE use only if locked!
void _queueWorkOnHandle(NSFileHandle* handle, NSMutableArray* waitingThreadArray) {
[waitingThreadArray insertObject: handle
atIndex: [waitingThreadArray count]];
}
// never called if single threaded.
- (void) workerThreadWillExit:(GSWWorkerThread*) thread
{
[_selfLock lock];
[_threads removeObject: thread];
if ([_waitingThreads count]) {
_workOnHandle([_waitingThreads objectAtIndex:0], self, _threads, _isMultiThreadEnabled);
[_waitingThreads removeObjectAtIndex:0];
}
[_selfLock unlock];
}
//PRIVATE!
// we are locked when we do this.
-(void) _lockedShutdownThreads
{
NS_DURING {
// loop throuh all threads and quit all not working ones until all shutdown
}
NS_HANDLER {
}
NS_ENDHANDLER;
}
//--------------------------------------------------------------------
-(void)unregisterForEvents
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSFileHandleConnectionAcceptedNotification
object:_fileHandle];
/* [NotificationDispatcher removeObserver:self
name: NSFileHandleConnectionAcceptedNotification
object:fileHandle];
*/
DESTROY(_fileHandle);
};
SYNCHRONIZED(_selfLock) {
NSAssert(_fileHandle,@"not registered");
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSFileHandleConnectionAcceptedNotification
object:_fileHandle];
_shouldGrow = NO;
[self _lockedShutdownThreads];
DESTROY(_fileHandle);
}
END_SYNCHRONIZED;
}
//--------------------------------------------------------------------
-(void)runOnce
{
//call doesBusyRunOnce
LOGObjectFnNotImplemented(); //TODOFN
};
//--------------------------------------------------------------------
-(BOOL)doesBusyRunOnce
@ -193,596 +254,61 @@ int allow_severity = LOG_INFO;
-(id)workerThreadCount
{
return GSWIntNumber(_workerThreadCount);
};
}
//--------------------------------------------------------------------
-(void)setWorkerThreadCount:(id)workerThreadCount
{
if ([self tryLock])
{
NS_DURING
{
_workerThreadCount=[workerThreadCount intValue];
}
NS_HANDLER
{
LOGException(@"%@ (%@)",
localException,
[localException reason]);
}
NS_ENDHANDLER;
[self unlock];
}
else
{
//TODO
};
};
//--------------------------------------------------------------------
-(void)setWorkerThreadCountMin:(id)workerThreadCount
-(void)announceNewConnection:(NSNotification*)notification
{
if ([self tryLock])
{
NS_DURING
{
_workerThreadCountMin=[workerThreadCount intValue];
if (_workerThreadCountMin<1)
_workerThreadCountMin=1;
}
NS_HANDLER
{
LOGException(@"%@ (%@)",
localException,
[localException reason]);
}
NS_ENDHANDLER;
[self unlock];
}
else
{
//TODO
};
};
//--------------------------------------------------------------------
-(id)workerThreadCountMin
{
return GSWIntNumber(_workerThreadCountMin);
};
//--------------------------------------------------------------------
-(void)setWorkerThreadCountMax:(id)workerThreadCount
{
if ([self tryLock])
{
NS_DURING
{
_workerThreadCountMax=[workerThreadCount intValue];
if (_workerThreadCountMax<1)
_workerThreadCountMax=1;
}
NS_HANDLER
{
LOGException(@"%@ (%@)",
localException,
[localException reason]);
}
NS_ENDHANDLER;
[self unlock];
}
else
{
//TODO
};
};
//--------------------------------------------------------------------
-(id)workerThreadCountMax
{
return GSWIntNumber(_workerThreadCountMax);
};
//--------------------------------------------------------------------
-(BOOL)isMultiThreadEnabled
{
return _isMultiThreadEnabled;
};
//--------------------------------------------------------------------
-(void)setListenQueueSize:(id)listenQueueSize
{
if ([self tryLock])
{
NS_DURING
{
_queueSize=[listenQueueSize intValue];
if (_queueSize<1)
_queueSize=1;
}
NS_HANDLER
{
LOGException(@"%@ (%@)",
localException,
[localException reason]);
}
NS_ENDHANDLER;
[self unlock];
}
else
{
//TODO
};
};
//--------------------------------------------------------------------
//NDFN
-(id)announceNewConnection:(NSNotification*)notification
{
GSWDefaultAdaptorThread* newThread=nil;
NSFileHandle* listenHandle=nil;
NSFileHandle* inStream = nil;
GSWTime requestTS=0;
NSFileHandle *listenHandle=nil;
NSFileHandle *inStream = nil;
NSString* connRefusedMessage=nil;
LOGObjectFnStart();
requestTS=GSWTime_now();
// requestTS=GSWTime_now();
listenHandle=[notification object];
NSDebugDeepMLLog(@"info",@"listenHandle=%p",(void*)listenHandle);
inStream = [[notification userInfo] objectForKey:@"NSFileHandleNotificationFileHandleItem"];
// we want future Notifications.
[listenHandle acceptConnectionInBackgroundAndNotify];
inStream = [[notification userInfo]objectForKey:@"NSFileHandleNotificationFileHandleItem"];
NSDebugDeepMLog(@"%@ announceNewConnection notification=%@ "
@"socketAddress=%@ [notification userInfo]=%p",
GSCurrentThread(),
notification,
[inStream socketAddress],
[notification userInfo]);
if (![self isConnectionAllowedWithHandle:inStream
returnedMessage:&connRefusedMessage])
{
NSDebugDeepMLog(@"DESTROY the connection: conn refused - "
@"%@ - A1 readInProgress=%d",
GSCurrentThread(),
(int)[_fileHandle readInProgress]);
[GSWDefaultAdaptorThread sendConnectionRefusedResponseToStream:inStream
withMessage:connRefusedMessage];
inStream=nil;
}
else
{
NSDebugDeepMLLog(@"info",@"notification userInfo=%@",
[notification userInfo]);
NSDebugDeepMLog(@"%@ - A1 readInProgress=%d",
GSCurrentThread(),
(int)[_fileHandle readInProgress]);
NSDebugDeepMLLog(@"%@ - A1 readInProgress=%d",
GSCurrentThread(),
(int)[_fileHandle readInProgress]);
NSDebugDeepMLog(@"NEW CONN APP _selfLockn=%d _selfLock_thread_id=%@ "
@"_globalLockn=%d _globalLock_thread_id=%@ "
@"threads count=%d waitingThreads count=%d blocked=%d",
(int)([GSWApplication application]->_selfLockn),
([GSWApplication application]->_selfLock_thread_id),
(int)([GSWApplication application]->_globalLockn),
([GSWApplication application]->_globalLock_thread_id),
[_threads count],
[_waitingThreads count],
_blocked);
NSDebugDeepMLog(@"[waitingThreads count]=%d queueSize=%d",
[_waitingThreads count],_queueSize);
if ([_waitingThreads count]>=_queueSize)
{
//remove expired thread
if ([self tryLock])
{
NSDebugMLog0(@"locked !");
NS_DURING
{
int i=0;
int waitingThreadsCount=[_waitingThreads count];
GSWDefaultAdaptorThread* thread=nil;
for(i=0;i<waitingThreadsCount;)
{
thread=[_waitingThreads objectAtIndex:i];
if ([thread isExpired])
{
// [GSWDefaultAdaptorThread sendRetryLasterResponseToStream:[thread stream]];
[_waitingThreads removeObjectAtIndex:i];
}
else
i++;
};
}
NS_HANDLER
{
LOGException(@"%@ (%@)",
localException,[localException reason]);
//TODO
[self unlock];
[localException raise];
}
NS_ENDHANDLER;
[self unlock];
};
};
if ([_waitingThreads count]>=_queueSize)
{
NSDebugDeepMLog(@"DESTROY the connection: too many conn - "
@"%@ - A1 readInProgress=%d",
GSCurrentThread(),
(int)[_fileHandle readInProgress]);
[GSWDefaultAdaptorThread sendRetryLasterResponseToStream:inStream];
inStream=nil;
}
else
{
//release done after lock !
newThread=[[GSWDefaultAdaptorThread alloc] initWithApp:[GSWApplication application]
withAdaptor:self
withStream:inStream];
if (newThread)
{
NSDebugLockMLog0(@"_newThread !");
[newThread setRequestTS:requestTS];
if ([self tryLock])
{
NSDebugLockMLog0(@"locked !");
NS_DURING
{
NSDebugLockMLLog(@"low",
@"[waitingThreads count]=%d [threads count]=%d",
[_waitingThreads count],
[_threads count]);
if ([_threads count]<_workerThreadCount
|| _isMultiThreadEnabled == NO)
{
[_threads addObject:newThread];
NSDebugLockMLLog(@"trace",@"isMultiThreadEnabled=%d",
_isMultiThreadEnabled);
if (_isMultiThreadEnabled)
{
[GSWApplication statusLogWithFormat:@"%@ : Lauch Thread (Multi) %@",
GSCurrentThread(),
GSWTime_format(GSWTime_now())];
NSDebugLockMLLog(@"info",
@"%@ : "
@"Lauch Thread (Multi) %p",
GSCurrentThread(),
(void*)newThread);
[NSThread detachNewThreadSelector:@selector(run:)
toTarget:newThread
withObject:nil];
DESTROY(newThread);
}
else
{
//Runit after
/*
[GSWApplication statusLogWithFormat:@"Lauch Thread (Mono)"];
NSDebugMLLog(@"info",
@"Lauch Thread (Mono) %p",
(void*)_newThread);
[_newThread run:nil];
*/
};
}
else
{
[GSWApplication statusLogString:@"Set Thread to wait"];
NSDebugLockMLLog(@"info",
@"Set Thread to wait %p",
(void*)newThread);
[_waitingThreads addObject:newThread];
DESTROY(newThread);
};
}
NS_HANDLER
{
LOGException(@"%@ (%@)",
localException,[localException reason]);
//TODO
[self unlock];
[localException raise];
}
NS_ENDHANDLER;
[self unlock];
}
else
{
DESTROY(newThread);
};
};
if (!_isMultiThreadEnabled && newThread)
{
//NSDebugLockMLLog(@"info",
// @"Launch Thread (Mono) %@ %p",
// GSWTime_format(GSWTime_now()),
// (void*)newThread);
[newThread run:nil];
DESTROY(newThread);
//NSDebugLockMLLog(@"info",
// @"Stop Thread (Mono) %@",GSWTime_format(GSWTime_now()));
};
};
};
NSDebugLockMLLog(@"trace",@"Try Lock");
if ([self tryLock])
{
BOOL accept=YES;//NEW[waitingThreads count]<queueSize;
NSDebugLockMLLog(@"trace",@"Accept=%d",accept);
NS_DURING
{
if (accept)
{
[listenHandle acceptConnectionInBackgroundAndNotify];
_blocked=NO;
NSDebugDeepMLog(@"ACCEPT %@ A2 readInProgress=%d",
GSCurrentThread(),
(int)[_fileHandle readInProgress]);
}
else
{
NSDebugDeepMLog(@"NOT ACCEPT %@ A2 readInProgress=%d",
GSCurrentThread(),
(int)[_fileHandle readInProgress]);
};
NSDebugLockMLog(@"%@ A2 readInProgress=%d",
GSCurrentThread(),
(int)[_fileHandle readInProgress]);
}
NS_HANDLER
{
LOGException(@"%@ (%@)",
localException,[localException reason]);
//TODO
_blocked=!accept;
[self unlock];
[localException raise];
}
NS_ENDHANDLER;
_blocked=!accept;
printf("blocked=%d",_blocked);
[self unlock];
};
NSDebugLockMLLog(@"trace",@"end announceNewConnection");
NSDebugDeepMLog(@"END NEWCONN APP _selfLockn=%d _selfLock_thread_id=%@ "
@"_globalLockn=%d _globalLock_thread_id=%@ "
@"threads count=%d waitingThreads count=%d "
@"blocked=%d acceptOK",
(int)([GSWApplication application]->_selfLockn),
([GSWApplication application]->_selfLock_thread_id),
(int)([GSWApplication application]->_globalLockn),
([GSWApplication application]->_globalLock_thread_id),
[_threads count],
[_waitingThreads count],
_blocked);
LOGObjectFnStop();
return self;
};
//--------------------------------------------------------------------
-(void)adaptorThreadExited:(GSWDefaultAdaptorThread*)adaptorThread
returnedMessage:&connRefusedMessage]) {
// don't waste any time
[inStream closeFile];
return;
}
// SYNCHRONIZED(_selfLock)
{
LOGObjectFnStart();
// NSDebugMLLog(@"trace",@"adaptorThreadExited");
NSDebugDeepMLog0(@"adaptorThreadExited");
NSDebugDeepMLog(@"EXIT APP _selfLockn=%d _selfLock_thread_id=%@ "
@"_globalLockn=%d _globalLock_thread_id=%@ "
@"threads count=%d waitingThreads count=%d blocked=%d",
(int)([GSWApplication application]->_selfLockn),
([GSWApplication application]->_selfLock_thread_id),
(int)([GSWApplication application]->_globalLockn),
([GSWApplication application]->_globalLock_thread_id),
[_threads count],
[_waitingThreads count],
_blocked);
if ([self tryLock])
{
NSAutoreleasePool* pool=nil;
#ifndef NDEBUG
pool=[NSAutoreleasePool new];
GSWLogMemCF("New NSAutoreleasePool: %p",pool);
NSDebugLockMLLog(@"low",
@"remove thread %p",
(void*)adaptorThread);
GSWLogMemCF("Destroy NSAutoreleasePool: %p",pool);
DESTROY(pool);
#endif
NS_DURING
{
[adaptorThread retain];
[adaptorThread autorelease];
[_threads removeObject:adaptorThread];
}
NS_HANDLER
{
pool=[NSAutoreleasePool new];
GSWLogMemCF("New NSAutoreleasePool: %p",pool);
LOGException(@"%@ (%@)",
localException,
[localException reason]);
GSWLogMemCF("Destroy NSAutoreleasePool: %p",pool);
DESTROY(pool);
//TODO
// [self unlock];
// [localException raise];
}
NS_ENDHANDLER;
#ifndef NDEBUG
pool=[NSAutoreleasePool new];
GSWLogMemCF("New NSAutoreleasePool: %p",pool);
NSDebugLockMLLog(@"low",
@"[waitingThreads count]=%d [threads count]=%d",
[_waitingThreads count],
[_threads count]);
GSWLogMemCF("Destroy NSAutoreleasePool: %p",pool);
DESTROY(pool);
#endif
if ([_threads count]==0)
{
BOOL isApplicationRequestHandlingLocked=[[GSWApplication application] isRequestHandlingLocked];
if (isApplicationRequestHandlingLocked)
{
pool=[NSAutoreleasePool new];
GSWLogMemCF("New NSAutoreleasePool: %p",pool);
LOGSeriousError0(@"Application RequestHandling is LOCKED !!!");
NSAssert(NO,@"Application RequestHandling is LOCKED !!!");//TODO-NOW
[[GSWApplication application] terminate];
GSWLogMemCF("Destroy NSAutoreleasePool: %p",pool);
DESTROY(pool);
};
};
if ([_waitingThreads count]>0 && [_threads count]<_workerThreadCount)
{
NS_DURING
{
GSWDefaultAdaptorThread* thread=nil;
int waitingThreadsCount=[_waitingThreads count];
while(!thread && waitingThreadsCount>0)
{
thread=[_waitingThreads objectAtIndex:0];
if ([thread isExpired])
{
//[GSWDefaultAdaptorThread sendRetryLasterResponseToStream:[_thread stream]];
thread=nil;
}
else
[_threads addObject:thread];
[_waitingThreads removeObjectAtIndex:0];
waitingThreadsCount--;
};
if (thread)
{
#ifndef NDEBUG
pool=[NSAutoreleasePool new];
GSWLogMemCF("New NSAutoreleasePool: %p",pool);
[GSWApplication statusLogString:@"Lauch waiting Thread"];
NSDebugLockMLLog(@"info",
@"Lauch waiting Thread %p",
(void*)thread);
GSWLogMemCF("Destroy NSAutoreleasePool: %p",pool);
DESTROY(pool);
#endif
if (_isMultiThreadEnabled)
[NSThread detachNewThreadSelector:@selector(run:)
toTarget:thread
withObject:nil];
else
[thread run:nil];
};
}
NS_HANDLER
{
pool=[NSAutoreleasePool new];
GSWLogMemCF("New NSAutoreleasePool: %p",pool);
LOGException(@"%@ (%@)",
localException,
[localException reason]);
GSWLogMemCF("Destroy NSAutoreleasePool: %p",pool);
DESTROY(pool);
//TODO
// [self unlock];
// [localException raise];
}
NS_ENDHANDLER;
};
NS_DURING
{
BOOL accept=[_waitingThreads count]<_queueSize;
if (_blocked && accept)
{
NSDebugDeepMLog(@"ACCEPT AGAIN %@ A2 readInProgress=%d",
GSCurrentThread(),
(int)[_fileHandle readInProgress]);
[_fileHandle acceptConnectionInBackgroundAndNotify];
_blocked=NO;
};
}
NS_HANDLER
{
pool=[NSAutoreleasePool new];
GSWLogMemCF("New NSAutoreleasePool: %p",pool);
LOGException(@"%@ (%@)",
localException,
[localException reason]);
GSWLogMemCF("Destroy NSAutoreleasePool: %p",pool);
DESTROY(pool);
//TODO
// [self unlock];
// [localException raise];
}
NS_ENDHANDLER;
[self unlock];
};
NSDebugDeepMLog(@"END EXIT APP _selfLockn=%d _selfLock_thread_id=%@ "
@"_globalLockn=%d _globalLock_thread_id=%@ "
@"threads count=%d waitingThreads count=%d blocked=%d",
(int)([GSWApplication application]->_selfLockn),
([GSWApplication application]->_selfLock_thread_id),
([GSWApplication application]->_globalLockn),
([GSWApplication application]->_globalLock_thread_id),
[_threads count],
[_waitingThreads count],
_blocked);
// (int)(((UnixFileHandle*)fileHandle)->acceptOK));
NSDebugLockMLog(@"%@ B2 readInProgress=%d",
GSCurrentThread(),(int)[_fileHandle readInProgress]);
LOGObjectFnStop();
};
[_selfLock lock];
int count = [_threads count];
if (count < _workerThreadCountMax) {
_workOnHandle(inStream,self, _threads, _isMultiThreadEnabled);
} else {
_queueWorkOnHandle(inStream, _waitingThreads);
}
[_selfLock unlock];
}
// END_SYNCHRONIZED;
}
-(NSFileHandle*)fileHandle
{
return _fileHandle;
};
//--------------------------------------------------------------------
//NDFN
}
-(id)announceBrokenConnection:(id)notification
{
LOGObjectFnNotImplemented(); //TODOFN
NSDebugMLLog(@"trace",@"announceBrokenConnection");
// [self shutDownConnectionWithSocket:[in_port _port_socket]];
return self;
};
}
//--------------------------------------------------------------------
// lock
-(BOOL)tryLock
{
BOOL locked=NO;
LOGObjectFnStart();
NSDebugLockMLog(@"self=%p %@ TRYLOCK",
self, GSCurrentThread());
locked=LoggedTryLockBeforeDate(_selfLock,
[NSDate dateWithTimeIntervalSinceNow:90]);
NSDebugLockMLog(@"self=%p %@ TRYLOCK LOCKED ?",
self, GSCurrentThread());
LOGObjectFnStop();
return locked;
};
//--------------------------------------------------------------------
// unlock
-(void)unlock
{
LOGObjectFnStart();
NSDebugLockMLog(@"self=%p %@ UNLOCK",
self, GSCurrentThread());
LoggedUnlock(_selfLock);
NSDebugLockMLog(@"self=%p %@ UNLOCK UNLOCKED ?",
self, GSCurrentThread());
LOGObjectFnStop();
};
-(BOOL)isConnectionAllowedWithHandle:(NSFileHandle*)handle
returnedMessage:(NSString**)retMessage
@ -791,18 +317,9 @@ int allow_severity = LOG_INFO;
if ([_adaptorHost length]>0)
{
NSString* connAddress=[handle socketAddress];
NSDebugMLog(@"HANDLE connAddress: %@ _adaptorHost=%@",
connAddress,_adaptorHost);
if ([connAddress isEqualToString:_adaptorHost])
{
[GSWApplication statusDebugWithFormat:
@"ACCEPTED connection from: %@ (Allowed: %@)",
connAddress,_adaptorHost];
}
else
{
[GSWApplication statusLogErrorWithFormat:
@"REFUSED connection from: %@ (Allowed: %@)",
if ([connAddress isEqualToString:_adaptorHost] == NO) {
[GSWApplication statusLogErrorWithFormat:@"REFUSED connection from: %@ (Allowed: %@)",
connAddress,_adaptorHost];
allowed=NO;
if (retMessage)
@ -849,6 +366,12 @@ int allow_severity = LOG_INFO;
return allowed;
};
// this is not changed after init, so there is no need for a lock
- (BOOL) isMultiThreadEnabled
{
return _isMultiThreadEnabled;
}
@end

View file

@ -1,87 +0,0 @@
/** GSWDefaultAdaptorThread.h - <title>GSWeb: Class GSWDefaultAdaptorThread</title>
Copyright (C) 1999-2003 Free Software Foundation, Inc.
Written by: Manuel Guesdon <mguesdon@orange-concept.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.
**/
// $Id$
#ifndef _GSWDefaultAdaptorThread_h__
#define _GSWDefaultAdaptorThread_h__
//==============================================================================
@interface GSWDefaultAdaptorThread: NSObject
{
GSWApplication* _application;
GSWAdaptor* _adaptor;
NSFileHandle* _stream;
NSAutoreleasePool* _pool;
BOOL _keepAlive;
NSRunLoop* _currentRunLoop;
NSDate* _runLoopDate;
BOOL _isMultiThread;
GSWTime _requestTS;
GSWTime _creationTS;
GSWTime _runTS;
GSWTime _beginDispatchRequestTS;
GSWTime _endDispatchRequestTS;
GSWTime _sendResponseTS;
NSString* _remoteAddress;
int _requestNamingConv;//GSWNAMES_INDEX or WONAMES_INDEX
}
-(id)initWithApp:(GSWApplication*)application
withAdaptor:(GSWAdaptor*)adaptor
withStream:(NSFileHandle*)stream;
-(void)run:(id)nothing;
-(GSWAdaptor*)adaptor;
-(NSAutoreleasePool*)pool;
-(void)setPool:(NSAutoreleasePool*)pool
destroyLast:(BOOL)destroy;
-(BOOL)readRequestReturnedRequestLine:(NSString**)requestLine
returnedHeaders:(NSDictionary**)headers
returnedData:(NSData**)data;
-(GSWRequest*)createRequestFromRequestLine:(NSString*)requestLine
headers:(NSDictionary*)headers
data:(NSData*)data;
-(void)sendResponse:(GSWResponse*)response;
-(void)threadExited;
+(id)threadExited:(NSNotification*)notif;
-(GSWTime)creationTS;
-(BOOL)isExpired;
-(void)setRequestTS:(GSWTime)requestTS;
+(void)sendResponse:(GSWResponse*)response
toStream:(NSFileHandle*)aStream
withNamingConv:(int)requestNamingConv
withAdditionalHeaderLines:(NSArray*)addHeaders
withRemoteAddress:(NSString*)remoteAddress;
+(void)sendRetryLasterResponseToStream:(NSFileHandle*)stream;
+(void)sendConnectionRefusedResponseToStream:(NSFileHandle*)stream
withMessage:(NSString*)message;
@end
#endif

View file

@ -1,884 +0,0 @@
/** GSWDefaultAdaptorThread.m - <title>GSWeb: Class GSWDefaultAdaptorThread</title>
Copyright (C) 1999-2004 Free Software Foundation, Inc.
Written by: Manuel Guesdon <mguesdon@orange-concept.com>
Date: Feb 1999
$Revision$
$Date$
$Id$
<abstract></abstract>
This file is part of the GNUstep Web Library.
<license>
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.
</license>
**/
#include "config.h"
RCS_ID("$Id$")
#include "GSWeb.h"
#include <unistd.h>
#include <math.h> //for fabs
#include "NSNonBlockingFileHandle.h"
#define ADAPTOR_THREAD_TIME_OUT (5*60) // in seconds. threads waiting for more than 5 minutes are not processed
static SEL objectAtIndexSEL=NULL;
static SEL appendDataSEL=NULL;
static NSData* lineFeedData=nil;
//====================================================================
@implementation GSWDefaultAdaptorThread
+ (void) initialize
{
if (self == [GSWDefaultAdaptorThread class])
{
objectAtIndexSEL=@selector(objectAtIndex:);
appendDataSEL=@selector(appendData:);
ASSIGN(lineFeedData,([[NSString stringWithString:@"\n"]
dataUsingEncoding:NSASCIIStringEncoding]));
};
};
//--------------------------------------------------------------------
-(id)init
{
if ((self=[super init]))
{
_creationTS=GSWTime_now();
_requestNamingConv=GSWebNamingConv;//GSWNAMES_INDEX or WONAMES_INDEX
};
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];
};
return self;
};
//--------------------------------------------------------------------
-(void)dealloc
{
DESTROY(_stream);
DESTROY(_remoteAddress);
DESTROY(_pool);
[super dealloc];
};
//--------------------------------------------------------------------
-(GSWAdaptor*)adaptor
{
return _adaptor;
};
//--------------------------------------------------------------------
-(NSAutoreleasePool*)pool
{
return _pool;
};
//--------------------------------------------------------------------
-(void)setPool:(NSAutoreleasePool*)pool
destroyLast:(BOOL)destroy
{
if (destroy) {
DESTROY(_pool);
};
_pool=pool;
};
//--------------------------------------------------------------------
-(void)run:(id)nothing
{
BOOL requestOk=NO;
NSMutableDictionary* threadDictionary=nil;
NSString* requestLine=nil;
NSDictionary* headers=nil;
NSData* data=nil;
DESTROY(_pool);
_pool=[NSAutoreleasePool new];
#ifdef GSWDEBUG_DEEP
[GSWApplication logString:@"pool allocated!"];
#endif
_runTS=GSWTime_now();
_beginDispatchRequestTS=GSWTime_zero();
_endDispatchRequestTS=GSWTime_zero();
_sendResponseTS=GSWTime_zero();
#ifdef GSWDEBUG_DEEP
[GSWApplication statusLogString:@"Thread run START"];
#endif
if (_isMultiThread)
{
threadDictionary=GSCurrentThreadDictionary();
[threadDictionary setObject:self
forKey:GSWThreadKey_DefaultAdaptorThread];
[[NSNotificationCenter defaultCenter] addObserver:[self class]
selector:@selector(threadExited:)
name:NSThreadWillExitNotification
object:[NSThread currentThread]];
/*
[NotificationDispatcher addObserver:[self class]
selector:@selector(threadExited:)
name:NSThreadWillExitNotification
object:[NSThread currentThread]];
*/
};
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;
NS_DURING
{
request=[self createRequestFromRequestLine:requestLine
headers:headers
data:data];
//NSLog(@"%s %@",__PRETTY_FUNCTION__, request);
}
NS_HANDLER
{
LOGException(@"GSWDefaultAdaptorThread: createRequestFromData Exception:%@ (%@)",
localException,[localException reason]);
}
NS_ENDHANDLER;
if (request)
{
//call application resourceRequestHandlerKey (retourne wr)
//call requets requestHandlerKey (retorune nil)
_beginDispatchRequestTS=GSWTime_now();
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;
_endDispatchRequestTS=GSWTime_now();
if (!response)
{
response=[GSWResponse responseWithMessage:@"Application returned no response"
inContext:nil
forRequest:request];
[response _finalizeInContext:nil]; //DO Call _finalizeInContext: !
};
if (response)
{
RETAIN(response);
NS_DURING
{
[self sendResponse:response];
}
NS_HANDLER
{
LOGException(@"GSWDefaultAdaptorThread: sendResponse Exception:%@ (%@)",
localException,
[localException reason],
[localException userInfo]);
}
NS_ENDHANDLER;
AUTORELEASE(response);
};
};
};
[_application threadWillExit];
if (_isMultiThread)
{
NSAssert([NSThread isMultiThreaded],@"No MultiThread !");
[NSThread exit]; //???
}
else
[self threadExited];
};
//--------------------------------------------------------------------
-(void)threadExited
{
[_adaptor adaptorThreadExited:self];
[self setPool:nil
destroyLast:YES];
[GSWApp debugAdaptorThreadExited];
};
//--------------------------------------------------------------------
+(id)threadExited:(NSNotification*)notif
{
NSThread* thread=nil;
NSMutableDictionary* threadDict=nil;
GSWDefaultAdaptorThread* adaptorThread=nil;
NSAssert([NSThread isMultiThreaded],@"No MultiThread !");
thread=[notif object];
threadDict = [thread threadDictionary];
adaptorThread=[threadDict objectForKey:GSWThreadKey_DefaultAdaptorThread];
[threadDict removeObjectForKey:GSWThreadKey_DefaultAdaptorThread];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSThreadWillExitNotification
object:thread];
/* [NotificationDispatcher removeObserver:self
name:NSThreadWillExitNotification
object:_thread];
*/
[adaptorThread threadExited];
return nil; //??
};
NSMutableArray* unpackData(NSMutableData* data)
{
NSMutableArray* lines = [NSMutableArray new];
int length = [data length];
if (length>0) {
NSRange range=NSMakeRange(0,0);
int i=0;
int lastIndex=0;
NSString * tmpString = nil;
char* dataBytes=(char*)[data mutableBytes];
BOOL endHeaders=NO;
while(!endHeaders && i<length)
{
if (dataBytes[i]=='\n') {
range.location=lastIndex;
range.length=(i-range.location);
lastIndex = i+1;
if (range.length > 1) {
tmpString=[[NSString alloc] initWithData: [data subdataWithRange:range]
encoding:NSASCIIStringEncoding];
[lines addObject: [tmpString stringByTrimmingSpaces]];
[tmpString release];
} else {
endHeaders=YES;
}
i++;
} else {
i++;
}
}
}
return [lines autorelease];
}
- (NSDictionary*) unpackHeaders:(NSArray*) lines
{
NSMutableDictionary* headers = [NSMutableDictionary dictionary];
int count = 0;
int i = 0;
NSArray* prevValue = nil;
if ((lines) && ([lines count] > 1)) {
count = [lines count];
for (i=1;i<count-1;i++) {
NSString * tmpLine = [lines objectAtIndex:i];
NSArray * components = [tmpLine componentsSeparatedByString:@": "];
NSString * value = nil;
NSArray * newValue = nil;
NSString * key = nil;
if ((components) && ([components count] == 2)) {
value = [components objectAtIndex:1];
key = [components objectAtIndex:0];
key = [[key stringByTrimmingSpaces] lowercaseString];
if ([key isEqualToString:GSWHTTPHeader_AdaptorVersion[GSWNAMES_INDEX]]
|| [key isEqualToString:GSWHTTPHeader_ServerName[GSWNAMES_INDEX]]) {
_requestNamingConv=GSWNAMES_INDEX;
goto keyDone;
}
if ([key isEqualToString:GSWHTTPHeader_AdaptorVersion[WONAMES_INDEX]]
|| [key isEqualToString:GSWHTTPHeader_ServerName[WONAMES_INDEX]]) {
_requestNamingConv=WONAMES_INDEX;
goto keyDone;
}
keyDone:
prevValue=[headers objectForKey:key];
if (prevValue) {
newValue=[prevValue arrayByAddingObject:value];
} else {
newValue=[NSArray arrayWithObject:value];
}
[headers setObject: newValue
forKey: key];
}
}
}
return headers;
}
- (NSData*) _readPostDataMultipart:(BOOL) multip boundary:(NSString*) boundary
{
#define UPLOAD_LIMIT 1024*1024*10 // 10 MB
#define TIME_LIMIT 15 // seconds
time_t starttime, now;
BOOL crSeen = NO;
BOOL nlSeen = NO;
BOOL crnlSeen = NO;
int crlfCount = 1;
int tmpPos = 0;
unsigned bytesSinceNewLine = 0;
BOOL isElapsed = NO;
NSMutableData* allMimeData = (NSMutableData*)[NSMutableData data];
NSData* boundaryData = nil;
unsigned boundaryLen = NSNotFound;
char buffer[5];
char buffer1[100];
if (multip) {
NSString * totalString = [[@"--" stringByAppendingString: boundary] stringByAppendingString:@"--"];
boundaryLen = [totalString length];
boundaryData = [totalString dataUsingEncoding: NSUTF8StringEncoding];
}
time(&starttime);
NS_DURING
while ((! isElapsed) && ([allMimeData length] <= UPLOAD_LIMIT) && (crlfCount > 0)) {
NSData* dataBlock = [_stream readDataOfLength:1];
if ((dataBlock) && ([dataBlock length])) {
[dataBlock getBytes:buffer];
if (buffer[0] == 0xd) {
crSeen = YES;
time(&now);
isElapsed = ((now - starttime) > TIME_LIMIT);
} else {
if (buffer[0] == 0xa) {
nlSeen = YES;
} else {
crSeen = NO;
nlSeen = NO;
crnlSeen = NO;
bytesSinceNewLine++;
}
}
if (crlfCount > 0) {
[allMimeData appendData:dataBlock];
// printf("%c (%x)\n", buffer[0], buffer[0]); // cr (0d) + nl (0a)
}
if (crSeen && nlSeen) {
crnlSeen = YES;
crSeen = NO;
nlSeen = NO;
if (boundaryLen == bytesSinceNewLine) {
NSRange range = NSMakeRange([allMimeData length]-2-boundaryLen, boundaryLen);
NSData * tmpData = [allMimeData subdataWithRange: range];
if ([tmpData isEqual: boundaryData]) {
crlfCount = 0;
}
}
tmpPos = [allMimeData length];
if (multip == NO) {
crlfCount = 0;
}
bytesSinceNewLine = 0;
}
}
}
NS_HANDLER
NSLog(@"%@", [localException reason]);
NS_ENDHANDLER
if ([allMimeData length] == 0) {
return nil;
}
return allMimeData;
}
//--------------------------------------------------------------------
/** read request from crrent stream and put request line, headers and data in
'requestLinePtr', 'headersPtr', 'dataPtr'. Returns YES if it's OK, NO otherwise
**/
-(BOOL)readRequestReturnedRequestLine:(NSString**)requestLinePtr
returnedHeaders:(NSDictionary**)headersPtr
returnedData:(NSData**)dataPtr
{
NSMutableData* pendingData=nil;
NSData* dataBlock=nil;
NSData* dataBlock2=nil;
int totalBytes=0;
int tries=0;
int dataBlockLength=0;
int contentLength=-1;
BOOL newLineSeen=NO;
BOOL allDataRead=NO;
BOOL isElapsed=NO;
BOOL headersDone=NO;
NSArray *listItems = nil;
NSMutableData * allMimeData = nil;
NSDictionary * headerDict = nil;
#define MAX_HEADER_BYTES 1000
time_t starttime, now;
if (!_stream)
{
ExceptionRaise0(@"GSWDefaultAdaptorThread",@"no stream");
}
time(&starttime);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
setsockopt([_stream fileDescriptor], SOL_SOCKET, SO_RCVTIMEO, &timeout,sizeof(timeout));
while (((allDataRead == NO) && (isElapsed == NO)) && (totalBytes < MAX_HEADER_BYTES)) {
char buffer[5];
dataBlock= [_stream readDataOfLength:1];
dataBlockLength=[dataBlock length];
if (dataBlockLength>0) {
[dataBlock getBytes:buffer];
if ((buffer[0] == 0xa)) {
if ((newLineSeen)) {
headersDone = YES;
}
newLineSeen = YES;
} else {
if ((buffer[0] != 0xd)) { // cr
newLineSeen = NO;
}
}
buffer[1] = '\0';
if (headersDone) {
NSArray * myArray = nil;
listItems = unpackData(pendingData);
headerDict = [self unpackHeaders:listItems];
*headersPtr = headerDict;
myArray = [headerDict objectForKey:@"content-length"];
if ((myArray) && ([myArray count])) {
contentLength = [[myArray objectAtIndex:0] intValue];
//
if (contentLength > 0) {
if (!allMimeData) {
allMimeData = (NSMutableData*)[NSMutableData data];
}
while ((contentLength > 0) && (! isElapsed)) {
dataBlock2 = [_stream readDataOfLength: contentLength];
[allMimeData appendData:dataBlock2];
contentLength = contentLength-[dataBlock2 length];
time(&now);
isElapsed = ((now - starttime) > 30);
}
}
//
} else { // no content length info
myArray = [headerDict objectForKey:@"content-type"];
if ((myArray) && ([myArray count])) {
NSString * multiStr = [myArray objectAtIndex:0];
BOOL multipart = ([multiStr hasPrefix:@"multipart/form-data;"]);
NSString * boundStr = nil;
if (multipart) {
boundStr = [multiStr substringFromIndex:30];
}
allMimeData = [self _readPostDataMultipart: multipart boundary: boundStr];
}
}
allDataRead = YES;
} else {
totalBytes+=dataBlockLength;
if (!pendingData)
pendingData=(NSMutableData*)[NSMutableData data];
[pendingData appendData:dataBlock];
tries=0;
}
} else {
if (((totalBytes>2) && (tries>3)) && (newLineSeen)) {
allDataRead = YES;
}
tries++;
}
}
if (allMimeData) {
*dataPtr = [[allMimeData retain] autorelease];
}
// check headers for contents?
if (!headerDict) {
*requestLinePtr=nil;
*headersPtr=nil;
*dataPtr=nil;
return NO;
}
*requestLinePtr = [listItems objectAtIndex:0];
return YES;
}
//--------------------------------------------------------------------
/** return a created request build with 'requestLine', 'headers' and 'data' **/
-(GSWRequest*)createRequestFromRequestLine:(NSString*)requestLine
headers:(NSDictionary*)headers
data:(NSData*)data
{
GSWRequest* request=nil;
int requestLineLength=0;
requestLineLength=[requestLine length];
if (requestLineLength==0)
{
ExceptionRaise(@"GSWDefaultAdaptorThread",
@"bad request first line: '%@'",
requestLine);
}
else
{
NSRange spaceRange;
NSRange urlRange;
NSString* method=nil;
NSString* url=nil;
NSString* protocolString=nil;
NSArray* protocol=nil;
spaceRange=[requestLine rangeOfString:@" "];
if (spaceRange.length==0 || spaceRange.location+spaceRange.length>=requestLineLength)
{
ExceptionRaise(@"GSWDefaultAdaptorThread",
@"bad request first line: No method or no protocol '%@'",
requestLine);
}
else
{
method=[requestLine substringToIndex:spaceRange.location];
urlRange.location=spaceRange.location+spaceRange.length;//+1 to skip space
spaceRange=[requestLine rangeOfString:@" "
options:NSBackwardsSearch
range:NSMakeRange(urlRange.location,requestLineLength-urlRange.location)];
if (spaceRange.length==0 || spaceRange.location<=urlRange.location)
{
ExceptionRaise(@"GSWDefaultAdaptorThread",
@"bad request first line: No protocol or no url '%@'",
requestLine);
}
else
{
protocolString=[requestLine substringFromIndex:spaceRange.location+spaceRange.length];
protocol=[protocolString componentsSeparatedByString:@"/"];
urlRange.length=spaceRange.location-urlRange.location;
url=[requestLine substringFromRange:urlRange];
if ([protocol count]!=2)
{
ExceptionRaise0(@"GSWDefaultAdaptorThread",@"bad request first line (HTTP)");
}
else
{
NSString* httpVersion=[protocol objectAtIndex:1];
if ((httpVersion) && ([httpVersion length] > 3)) {
httpVersion = [httpVersion substringToIndex:3];
}
request=[_application createRequestWithMethod:method
uri:url
httpVersion:httpVersion
headers:headers
content:data
userInfo:nil];
/* };*/
};
};
};
};
return request;
};
//--------------------------------------------------------------------
/** Send response 'response' to current stream using current naming convention **/
-(void)sendResponse:(GSWResponse*)response
{
NSString* anHeader=nil;
_sendResponseTS=GSWTime_now();
// Based on requestTS
anHeader=[NSString stringWithFormat:@"%@: applicationThreadCreation=+%0.3fs applicationThreadRun=+%0.3fs applicationBeginDispatchRequest=+%0.3fs applicationEndDispatchRequest=+%0.3fs applicationDispatchRequest=%0.3fs applicationBeginSendResponse=+%0.3fs applicationTimeSpent=%0.3fs",
GSWHTTPHeader_AdaptorStats[_requestNamingConv],
GSWTime_floatSec(_creationTS-_requestTS),
GSWTime_floatSec(_runTS-_requestTS),
GSWTime_floatSec(_beginDispatchRequestTS-_requestTS),
GSWTime_floatSec(_endDispatchRequestTS-_requestTS),
GSWTime_floatSec(_endDispatchRequestTS-_beginDispatchRequestTS),
GSWTime_floatSec(_sendResponseTS-_requestTS),
GSWTime_floatSec(_sendResponseTS-_requestTS)];
[[self class]sendResponse:response
toStream:_stream
withNamingConv:_requestNamingConv
withAdditionalHeaderLines:[NSArray arrayWithObject:anHeader]
withRemoteAddress:_remoteAddress];
ASSIGN(_stream,nil);
};
//--------------------------------------------------------------------
/** send response 'response' to stream 'aStream' using naming convention 'requestNamingConv'
Note: the stream is closed at the end of the write
**/
+(void)sendResponse:(GSWResponse*)response
toStream:(NSFileHandle*)aStream
withNamingConv:(int)requestNamingConv
withAdditionalHeaderLines:(NSArray*)addHeaders
withRemoteAddress:(NSString*)remoteAddress
{
BOOL ok=YES;
[response willSend];
if (response)
{
IMP objectAtIndexIMP=NULL;
int addHeadersCount=[addHeaders count];
int headerN=0;
int headerNForKey=0;
NSMutableData* responseData=(NSMutableData*)[NSMutableData data];
IMP appendDataIMP=[responseData methodForSelector:appendDataSEL];
NSArray* headerKeys=[response headerKeys];
int headerKeysCount=[headerKeys count];
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[requestNamingConv]];
(*appendDataIMP)(responseData,appendDataSEL,
[head dataUsingEncoding:NSASCIIStringEncoding]);
objectAtIndexIMP=[headerKeys methodForSelector:objectAtIndexSEL];
for(headerN=0;headerN<headerKeysCount;headerN++)
{
int headersForKeyCount=0;
key=(*objectAtIndexIMP)(headerKeys,objectAtIndexSEL,headerN);
headersForKey=[response headersForKey:key];
headersForKeyCount=[headersForKey count];
for(headerNForKey=0;headerNForKey<headersForKeyCount;headerNForKey++)
{
anHeader=[NSString stringWithFormat:@"%@: %@\n",
key,
[headersForKey objectAtIndex:headerNForKey]];
(*appendDataIMP)(responseData,appendDataSEL,
[anHeader dataUsingEncoding:NSASCIIStringEncoding]);
};
};
objectAtIndexIMP=[addHeaders methodForSelector:objectAtIndexSEL];
for(headerN=0;headerN<addHeadersCount;headerN++)
{
(*appendDataIMP)(responseData,appendDataSEL,
[(*objectAtIndexIMP)(addHeaders,objectAtIndexSEL,headerN)
dataUsingEncoding:NSASCIIStringEncoding]);
(*appendDataIMP)(responseData,appendDataSEL,lineFeedData);
};
// Headers/Content separator
(*appendDataIMP)(responseData,appendDataSEL,lineFeedData);
NS_DURING
{
[aStream writeData:responseData];
}
NS_HANDLER
{
ok=NO;
LOGException(@"GSWDefaultAdaptorThread: sendResponse Exception:%@ (%@)",
localException,[localException reason]);
NSDebugMLog(@"EXCEPTION GSWDefaultAdaptorThread: sendResponse Exception:%@ (%@)",
localException,[localException reason]);
[GSWApplication statusLogString:@"\nException while sending response\n"];
}
NS_ENDHANDLER;
if (ok)
{
NSData* responseContent=[response content];
int responseContentLength=[responseContent length];
if (responseContentLength>0)
{
[responseData setLength:responseContentLength];
[responseData setData:responseContent];
NS_DURING
{
[aStream writeData:responseData];
}
NS_HANDLER
{
ok=NO;
LOGException(@"GSWDefaultAdaptorThread: sendResponse Exception:%@ (%@)",
localException,[localException reason]);
[GSWApplication statusLogString:@"\nException while sending response\n"];
}
NS_ENDHANDLER;
};
};
};
[aStream closeFile];
};
//--------------------------------------------------------------------
/** Returns thread creation TS **/
-(GSWTime)creationTS
{
return _creationTS;
};
//--------------------------------------------------------------------
/** Returns YES if the thread has expired (timed out) **/
-(BOOL)isExpired
{
time_t elapsedSeconds=GSWTime_secPart(GSWTime_now()-_creationTS);
BOOL isExpired=(elapsedSeconds>ADAPTOR_THREAD_TIME_OUT);
return isExpired;
};
//--------------------------------------------------------------------
-(void)setRequestTS:(GSWTime)requestTS
{
_requestTS=requestTS;
};
//--------------------------------------------------------------------
-(NSFileHandle*)stream
{
return _stream;
};
//--------------------------------------------------------------------
+(void)sendRetryLasterResponseToStream:(NSFileHandle*)stream
{
GSWResponse* response=nil;
NSAutoreleasePool* pool=nil;
pool=[NSAutoreleasePool new];
response=[GSWResponse responseWithMessage:@"Temporary unavailable"
inContext:nil
forRequest:nil
forceFinalize:YES];
[response setStatus:503];//503=Service Unavailable
[self sendResponse:response
toStream:stream
withNamingConv:GSWNAMES_INDEX
withAdditionalHeaderLines:nil
withRemoteAddress:nil];
DESTROY(pool);
};
//--------------------------------------------------------------------
+(void)sendConnectionRefusedResponseToStream:(NSFileHandle*)stream
withMessage:(NSString*)message
{
GSWResponse* response=nil;
NSAutoreleasePool* pool=nil;
pool=[NSAutoreleasePool new];
response=[GSWResponse responseWithMessage:message
inContext:nil
forRequest:nil
forceFinalize:YES];
[response setStatus:503];//503=Service Unavailable
[self sendResponse:response
toStream:stream
withNamingConv:GSWNAMES_INDEX
withAdditionalHeaderLines:nil
withRemoteAddress:nil];
DESTROY(pool);
};
@end

View file

@ -44,5 +44,78 @@
#endif
/*
The following macros are for use of locks together with exception handling.
A synchronized block is properly 'unlocked' even if an exception occures.
It is used this way:
SYNCHRONIZED(MyObject) {
THROW(MyException..);
}
END_SYNCHRONIZED;
Where MyObject must be an object that conforms to the NSObject and NSLocking
protocol.
This is much different to
[MyObject lock];
{
THROW(MyException..);
}
[MyObject unlock];
which leaves the lock locked when an exception happens.
*/
#if defined(DEBUG_SYNCHRONIZED)
#define SYNCHRONIZED(__lock__) \
{ \
id<NSObject,NSLocking> __syncLock__ = [__lock__ retain]; \
[__syncLock__ lock]; \
fprintf(stderr, "0x%08X locked in %s.\n", \
(unsigned)__syncLock__, __PRETTY_FUNCTION__); \
NS_DURING {
#define END_SYNCHRONIZED \
} \
NS_HANDLER { \
fprintf(stderr, "0x%08X exceptional unlock in %s exception %s.\n", \
(unsigned)__syncLock__, __PRETTY_FUNCTION__,\
[[localException description] cString]); \
[__syncLock__ unlock]; \
[__syncLock__ release]; __syncLock__ = nil; \
[localException raise]; \
} \
NS_ENDHANDLER; \
fprintf(stderr, "0x%08X unlock in %s.\n", \
(unsigned)__syncLock__, __PRETTY_FUNCTION__); \
[__syncLock__ unlock]; \
[__syncLock__ release]; __syncLock__ = nil; \
}
#else
#define SYNCHRONIZED(__lock__) \
{ \
id<NSObject,NSLocking> __syncLock__ = [__lock__ retain]; \
[__syncLock__ lock]; \
NS_DURING {
#define END_SYNCHRONIZED \
} \
NS_HANDLER { \
[__syncLock__ unlock]; \
[__syncLock__ release]; \
[localException raise]; \
} \
NS_ENDHANDLER; \
[__syncLock__ unlock]; \
[__syncLock__ release]; \
}
#endif
#endif /* __GSWeb_GSWDefines_h__ */

View file

@ -0,0 +1,42 @@
/** GSWHTTPIO.h - GSWeb: Class GSWHTTPIO
Copyright (C) 2007 Free Software Foundation, Inc.
Written by: David Wetzel <dave@turbocat.de>
Date: 12.11.2007
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.
**/
#ifndef _GSWHTTPIO_h__
#define _GSWHTTPIO_h__
#import <Foundation/NSObject.h>
#include "GSWWOCompatibility.h"
@class NSFileHandle;
@class GSWRequest;
@interface GSWHTTPIO : NSObject {
}
+ (GSWRequest*) readRequestFromFromHandle:(NSFileHandle*) fh;
@end
#endif // _GSWHTTPIO_h__

374
GSWeb.framework/GSWHTTPIO.m Normal file
View file

@ -0,0 +1,374 @@
/** GSWHTTPIO.m - GSWeb: Class GSWHTTPIO
Copyright (C) 2007 Free Software Foundation, Inc.
Written by: David Wetzel <dave@turbocat.de>
Date: 12.11.2007
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.
**/
#include "GSWHTTPIO.h"
#include <Foundation/NSString.h>
#include <Foundation/NSFileHandle.h>
#include <Foundation/NSData.h>
#include <GNUstepBase/GSFileHandle.h>
#include <Foundation/NSError.h>
#include <Foundation/NSException.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/Foundation.h>
#include "GSWDefines.h"
#include "GSWConstants.h"
#include "GSWUtils.h"
#include "GSWDebug.h"
#include "GSWMessage.h"
#include "GSWResponse.h"
#include "GSWRequest.h"
#define READ_SIZE 1024
static NSString *URIResponseString = @" GNUstep Web\r\n";
static NSString *CONTENT_LENGTH = @"content-length";
static NSString *CONTENT_LENGTHCOLON = @"content-length: ";
static NSString *GET = @"GET";
static NSString *POST = @"POST";
static NSString *HEAD = @"HEAD";
static NSString *SPACE = @" ";
static NSString *HEADERSEP = @": ";
static NSString *NEWLINE = @"\r\n";
static NSString *NEWLINE2 = @"\r\n";
static NSString *HTTP11 = @"HTTP/1.1";
static NSString *CONNECTION = @"connection";
static NSString *KEEP_ALIVE = @"keep-alive";
static NSString *CLOSE = @"close";
/* Get error information.
*/
@interface NSError (GSCategories)
+ (NSError*) _last;
@end
@interface NSFileHandle (GSWFileHandleExtensions)
- (NSData*) readDataLine;
@end
@implementation NSFileHandle (GSWFileHandleExtensions)
- (NSData*) readDataLine
{
NSMutableData *d;
int got;
char buf[READ_SIZE];
d = [NSMutableData dataWithCapacity: READ_SIZE];
do {
//got = [(GSFileHandle*) self read: buf length: 1];
got = [(GSFileHandle*) self read: buf length: 1];
if (got > 0) {
if (buf[0] != 0xd) { // CR
if (buf[0] == 0xa) { // NL
return d;
}
[d appendBytes: buf length: got];
}
} else if (got < 0) {
[NSException raise: NSFileHandleOperationException
format: @"unable to read from descriptor - %@",
[NSError _last]];
}
} while (got > 0);
return d;
}
@end
@implementation GSWHTTPIO
// PRIVATE
void _unpackHeaderLineAddToDict(NSString *line, NSMutableDictionary* headers)
{
NSArray * components = [line componentsSeparatedByString:HEADERSEP];
NSString * value = nil;
NSArray * newValue = nil;
NSString * key = nil;
NSArray * prevValue = nil;
if ((components) && ([components count] == 2)) {
value = [components objectAtIndex:1];
key = [components objectAtIndex:0];
key = [[key stringByTrimmingSpaces] lowercaseString];
if ([key isEqualToString:GSWHTTPHeader_AdaptorVersion[GSWNAMES_INDEX]]
|| [key isEqualToString:GSWHTTPHeader_ServerName[GSWNAMES_INDEX]]) {
// _requestNamingConv=GSWNAMES_INDEX;
goto keyDone;
}
if ([key isEqualToString:GSWHTTPHeader_AdaptorVersion[WONAMES_INDEX]]
|| [key isEqualToString:GSWHTTPHeader_ServerName[WONAMES_INDEX]]) {
// _requestNamingConv=WONAMES_INDEX;
goto keyDone;
}
keyDone:
prevValue=[headers objectForKey:key];
if (prevValue) {
newValue=[prevValue arrayByAddingObject:value];
} else {
newValue=[NSArray arrayWithObject:value];
}
[headers setObject: newValue
forKey: key];
}
}
//PRIVATE
void _appendMessageHeaders(GSWMessage * message,NSMutableString * headers)
{
NSMutableDictionary * headerDict = [message headers];
NSArray * keyArray = nil;
int i = 0;
if (headerDict != nil) {
int count = 0;
if (![headerDict isKindOfClass:[NSMutableDictionary class]]) {
headerDict = [[headerDict mutableCopy] autorelease];
}
[headerDict removeObjectForKey:CONTENT_LENGTH];
keyArray = [headerDict allKeys];
count = [keyArray count];
for (; i < count; i++) {
NSString * currentKey = [keyArray objectAtIndex:i];
NSArray * currentValueArray = [headerDict objectForKey:currentKey];
if ([currentValueArray isKindOfClass:[NSArray class]]) {
int x = 0;
int valueCount = [currentValueArray count];
for (; x < valueCount; x++) {
[headers appendString:currentKey];
[headers appendString:HEADERSEP];
[headers appendString:[currentValueArray objectAtIndex:x]];
[headers appendString:NEWLINE];
}
} else {
NSString * myStrValue = (NSString*) currentValueArray;
[headers appendString:currentKey];
[headers appendString:HEADERSEP];
[headers appendString:myStrValue];
[headers appendString:NEWLINE];
}
}
}
}
//PRIVATE
void _sendMessage(GSWMessage * message, NSFileHandle* fh, NSString * httpVersion, GSWRequest * request, NSMutableString * headers)
{
int contentLength = 0;
BOOL keepAlive = NO;
BOOL requestIsHead = NO;
if (message) {
contentLength = [message _contentLength];
}
if (request) {
NSString * connectionValue = [request headerForKey:CONNECTION];
if (connectionValue) {
keepAlive = [connectionValue isEqualToString:KEEP_ALIVE];
}
requestIsHead = [[request method] isEqualToString:HEAD];
}
_appendMessageHeaders(message,headers);
if ([httpVersion isEqualToString:HTTP11]) {
if (keepAlive == NO) {
[headers appendString:@"connection: close\r\n"];
} else {
[headers appendString:@"connection: keep-alive\r\n"];
}
}
if (contentLength > 0) {
[headers appendString:CONTENT_LENGTHCOLON];
[headers appendString:[NSString stringWithFormat:@"%d\r\n", contentLength]];
}
[headers appendString:NEWLINE2];
[fh writeData: [headers dataUsingEncoding:NSISOLatin1StringEncoding
allowLossyConversion:YES]];
if ((requestIsHead == NO) && (contentLength > 0)) {
[fh writeData: [message content]];
}
}
+ (NSDictionary*) readHeadersFromHandle:(NSFileHandle*) fh
{
BOOL headersDone=NO;
NSArray *lines = [NSMutableArray array];
NSData *currentLineData = nil;
unsigned int length = 0;
NSMutableDictionary *headers = [NSMutableDictionary dictionary];
NSString * tmpString = nil;
NS_DURING {
while (YES) {
currentLineData = [fh readDataLine];
length = [currentLineData length];
if (length == 0) {
break;
}
tmpString=[[NSString alloc] initWithData: currentLineData
encoding:NSASCIIStringEncoding];
_unpackHeaderLineAddToDict(tmpString,headers);
[tmpString release]; tmpString = nil;
}
} NS_HANDLER {
NSLog(@"%s -- %@",__PRETTY_FUNCTION__, localException);
if (tmpString != nil) {
[tmpString release]; tmpString = nil;
}
headers = nil;
} NS_ENDHANDLER;
return headers;
}
// GET /infotext.html HTTP/1.1
// Host: www.example.net
+ (NSArray*) readRequestLineFromHandle:(NSFileHandle*) fh
{
NSString * tmpString = nil;
NSArray * components = nil;
NSData * currentLineData = nil;
int length = 0;
NS_DURING {
currentLineData = [fh readDataLine];
length = [currentLineData length];
if (length > 0) {
tmpString=[[NSString alloc] initWithData: currentLineData
encoding:NSASCIIStringEncoding];
components = [tmpString componentsSeparatedByString:@" "];
[tmpString release]; tmpString = nil;
}
} NS_HANDLER {
NSLog(@"%s -- %@",__PRETTY_FUNCTION__, localException);
if (tmpString != nil) {
[tmpString release]; tmpString = nil;
}
components = nil;
} NS_ENDHANDLER;
return components;
}
+ (NSData*) readContentFromFromHandle: fh
method: (NSString *) method
length: (int) length
{
NSData* data = nil;
if ((([method isEqualToString:GET]) || ([method isEqualToString:HEAD])) ||
([method isEqualToString:POST] == NO || (length <1))) {
return nil;
}
data = [fh readDataOfLength: length];
return data;
}
+ (GSWRequest*) readRequestFromFromHandle:(NSFileHandle*) fh
{
NSDictionary * headers;
NSArray * requestArray;
int contentLength = 0;
NSArray * tmpValue = nil;
NSString * method = nil;
NSData * contentData = nil;
GSWRequest * request = nil;
[fh setNonBlocking: NO];
requestArray = [self readRequestLineFromHandle:fh];
if ((!requestArray) || ([requestArray count] <3)) {
return nil;
}
headers = [self readHeadersFromHandle:fh];
if (!headers) {
return nil;
}
method = [requestArray objectAtIndex:0];
if ((tmpValue = [headers objectForKey:CONTENT_LENGTH]) && ([tmpValue count])) {
NSString * tmpString = [tmpValue objectAtIndex:0];
contentLength = [tmpString intValue];
contentData = [self readContentFromFromHandle: fh
method: [requestArray objectAtIndex:0]
length: contentLength];
}
request = [[GSWRequest alloc] initWithMethod:method
uri:[requestArray objectAtIndex:1]
httpVersion:[requestArray objectAtIndex:2]
headers:headers
content:contentData
userInfo:nil];
return [request autorelease];
}
+ (void) sendResponse:(GSWResponse*) response
toHandle:(NSFileHandle*) fh
request:(GSWRequest*) request
{
NSString * httpVersion = [response httpVersion];
NSMutableString * bufferStr = [NSMutableString string];
[bufferStr appendString:httpVersion];
[bufferStr appendString:SPACE];
[bufferStr appendString:GSWIntToNSString([response status])];
[bufferStr appendString:URIResponseString];
_sendMessage(response, fh, httpVersion, request, bufferStr);
}
@end

View file

@ -32,6 +32,9 @@
#ifndef _GSWMessage_h__
#define _GSWMessage_h__
@class GSWMessage;
@class GSWCookie;
typedef struct _GSWMessageIMPs
{
// Instance IMPs
@ -171,7 +174,6 @@ GSWEB_EXPORT NSString* GSWMessage_stringByConvertingToHTML(GSWMessage* aMessage,
//====================================================================
@interface GSWMessage (Cookies)
-(void)_finalizeCookiesInContext:(GSWContext*)aContext;
-(NSMutableArray*)_initCookies;
-(NSString*)_formattedCookiesString;
-(void)addCookie:(GSWCookie*)cookie;

View file

@ -32,6 +32,10 @@
#ifndef _GSWRequest_h__
#define _GSWRequest_h__
@class GSWDynamicURLString;
@class GSWContext;
#include "GSWMessage.h"
//====================================================================
/** A class to handle value and quality like for Accept-Language or

View file

@ -199,57 +199,47 @@ RCS_ID("$Id$")
content:(NSData*)content
userInfo:(NSDictionary*)userInfo
{
if ((self=[super init]))
{
if ((self=[super init])) {
NSString* adaptorVersion = nil;
if ((!aMethod) || ([aMethod length]==0))
{
ExceptionRaise(@"GSRequest",@"Empty/Null method during initialization");
}
else
{
if ((! anURL) || ([anURL length]==0))
{
ExceptionRaise(@"GSRequest",@"Empty/Null uri during initialization");
}
else
{
if ((! aVersion) || ([aVersion length]==0))
{
ExceptionRaise(@"GSRequest",@"Empty/Null http version during initialization");
}
else
{
ASSIGNCOPY(_method,aMethod);
[self setHTTPVersion:aVersion];
if ((!aMethod) || ([aMethod length]==0)) {
ExceptionRaise(@"GSWRequest",@"Empty/Null method during initialization");
}
[self setHeaders:headers];
_defaultFormValueEncoding=[[self class]defaultEncoding];
_applicationNumber=-9999;
{
NSString* adaptorVersion=[self headerForKey:GSWHTTPHeader_AdaptorVersion[GSWebNamingConv]];
if (!adaptorVersion)
adaptorVersion=[self headerForKey:GSWHTTPHeader_AdaptorVersion[GSWebNamingConvInversed]];
[self _setIsUsingWebServer:(adaptorVersion!=nil)];
};
_uri = [[GSWDynamicURLString alloc]initWithString:anURL];
[_uri checkURL];
if (([aMethod isEqualToString:@"GET"] == NO) && ([aMethod isEqualToString:@"POST"] == NO) &&
([aMethod isEqualToString:@"HEAD"] == NO)) {
if (!content)
content = [NSData data];
// if ([content isKindOfClass:[GSWInputStreamData class]])
[self setContent:content];
// else
// [self appendContentData:content];
ExceptionRaise(@"GSWRequest", @"Method '%@' is not supported. To support '%@', you will have to implement a subclass of GSWRequest, and force GSWeb to instantiate it.", aMethod, aMethod);
}
[self setUserInfo:userInfo];
};
};
};
};
if ((! anURL) /*|| ([anURL length]==0)*/) {
ExceptionRaise(@"GSWRequest",@"Empty/Null uri during initialization");
}
if ((! aVersion) || ([aVersion length]==0)) {
ExceptionRaise(@"GSWRequest",@"Empty/Null http version during initialization");
}
ASSIGNCOPY(_method,aMethod);
[self setHTTPVersion:aVersion];
[self setHeaders:headers];
_defaultFormValueEncoding=[[self class] defaultEncoding];
_applicationNumber=-9999;
adaptorVersion=[self headerForKey:GSWHTTPHeader_AdaptorVersion[GSWebNamingConv]];
if (!adaptorVersion) {
adaptorVersion=[self headerForKey:GSWHTTPHeader_AdaptorVersion[GSWebNamingConvInversed]];
}
[self _setIsUsingWebServer:(adaptorVersion!=nil)];
_uri = [[GSWDynamicURLString alloc] initWithString:anURL];
[_uri checkURL];
if (!content)
content = [NSData data];
[self setContent:content];
[self setUserInfo:userInfo];
}
return self;
};

View file

@ -32,23 +32,18 @@
#ifndef _GSWRequestHandler_h__
#define _GSWRequestHandler_h__
#define DidHandleRequestNotification "WORequestHandlerDidHandleRequestNotification"
//====================================================================
@interface GSWRequestHandler : NSObject <NSLocking>
{
NSRecursiveLock* _selfLock;
#ifndef NDEBUG
int _selfLockn;
#endif
};
}
-(GSWResponse*)handleRequest:(GSWRequest*)aRequest;
-(void)lock;
-(void)unlock;
@end
//====================================================================
@interface GSWRequestHandler (GSWRequestHandlerClassA)
+(id)handler;
@end
#endif //_GSWRequestHandler_h__

View file

@ -40,29 +40,25 @@ RCS_ID("$Id$")
-(GSWResponse*)handleRequest:(GSWRequest*)aRequest
{
return [self subclassResponsibility:_cmd];
};
}
//--------------------------------------------------------------------
-(void)lock
{
LOGObjectFnNotImplemented(); //TODOFN
};
}
//--------------------------------------------------------------------
-(void)unlock
{
LOGObjectFnNotImplemented(); //TODOFN
};
}
@end
//====================================================================
@implementation GSWRequestHandler (GSWRequestHandlerClassA)
+(id)handler
{
[self subclassResponsibility:_cmd];
return nil;
};
}
@end

View file

@ -32,6 +32,10 @@
#ifndef _GSWResponse_h__
#define _GSWResponse_h__
@class GSWResponse;
@class GSWContext;
@class GSWRequest;
typedef struct _GSWResponseIMPs
{
// Instance IMPs

View file

@ -69,7 +69,6 @@
#define GSWComponentRequestHandler WOComponentRequestHandler
#define GSWResourceRequestHandler WOResourceRequestHandler
#define GSWDirectActionRequestHandler WODirectActionRequestHandler
#define GSWDefaultAdaptorThread WODefaultAdaptorThread
#define GSWKeyValueAssociation WOKeyValueAssociation
#define GSWConstantValueAssociation WOConstantValueAssociation
#define GSWHTMLStaticElement WOHTMLStaticElement

View file

@ -0,0 +1,87 @@
/** Implementation GSWWorkerThread for GNUStep
Copyright (C) 2007 Free Software Foundation, Inc.
Written by: David Wetzel <dave@turbocat.de>
Date: 2007
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 Lesser General Public
License as published by the Free Software Foundation; either
version 3 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 Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
// $Id: GSWToggle.h 14202 2002-07-27 23:48:47Z mguesdon $
#ifndef _GSWWorkerThread_h__
#define _GSWWorkerThread_h__
#import <Foundation/NSObject.h>
#include "GSWWOCompatibility.h"
@class GSWApplication;
@class GSWDefaultAdaptor;
@class NSRunLoop;
@class NSFileHandle;
@class NSThread;
@class NSAutoreleasePool;
@class GSWAdaptor;
@interface GSWWorkerThread: NSObject
{
GSWApplication *_app;
GSWDefaultAdaptor *_mtAdaptor; // 4.5
NSRunLoop *_currentRunLoop; // 4.5
NSFileHandle * _serverSocket; // rename to _serverFileHandle
// called int _currentSocket in 4.5
NSFileHandle *_currentSocket; // rename to _currentFileHandle
NSThread * _t;
NSAutoreleasePool * _pool;
BOOL _keepAlive; // 4.5
int _maxSocketIdleTime;
BOOL _errorOnRead;
BOOL _dispatchError;
BOOL _runFlag;
BOOL _processingRequest;
BOOL _restricted;
long _expTime;
int _reqCount;
BOOL _logOnce;
BOOL _isMultiThreadEnabled; // gsw only
// verified in 4.5:
// id _selfId;
// WODefaultAdaptor *_mtAdaptor;
// NSRunLoop *_currentRunLoop;
// int _currentSocket;
// id _inputBuffer;
// int _inputBufferLength;
// int _inputBufferIndex;
// BOOL _keepAlive;
// BOOL _errorOnRead;
// BOOL _runFlag;
}
-(id)initWithApp:(GSWApplication*)application
adaptor:(GSWAdaptor*)adaptor
stream:(NSFileHandle*)stream;
-(void)runOnce;
@end
#endif // _GSWWorkerThread_h__

236
GSWeb.framework/GSWWorkerThread.m Executable file
View file

@ -0,0 +1,236 @@
/** Implementation GSWWorkerThread for GNUStep
Copyright (C) 2007 Free Software Foundation, Inc.
Written by: David Wetzel <dave@turbocat.de>
Date: 1997
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 Lesser General Public
License as published by the Free Software Foundation; either
version 3 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 Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#include <Foundation/Foundation.h>
#include <Foundation/NSThread.h>
#include "GSWWOCompatibility.h"
#include "GSWWorkerThread.h"
#include "GSWDefines.h"
#include "GSWConstants.h"
#include "GSWUtils.h"
#include "GSWDebug.h"
#include "GSWRequest.h"
#include "GSWApplication.h"
#include "GSWAdaptor.h"
#include "GSWDefaultAdaptor.h"
#include "GSWResponse.h"
#include "GSWHTTPIO.h"
static NSData* lineFeedData=nil;
static NSString *REQUEST_ID = @"x-webobjects-request-id";
@interface NSThread (WeKnowWhatWeDo)
- (void)run;
@end
@implementation GSWWorkerThread
+ (void) initialize
{
if (self == [GSWWorkerThread class])
{
// ASSIGN(lineFeedData,([[NSString stringWithString:@"\n"] dataUsingEncoding:NSASCIIStringEncoding]));
}
}
-(void)dealloc
{
// TODO: add vars!
DESTROY(_serverSocket);
DESTROY(_currentSocket);
// NSThread * _t;
_app = nil;
_mtAdaptor = nil;
DESTROY(_pool);
[super dealloc];
}
-(id)initWithApp:(GSWApplication*)application
adaptor:(GSWAdaptor*)adaptor
stream:(NSFileHandle*)stream
{
if ((self = [self init])) {
_app = application;
_mtAdaptor = (GSWDefaultAdaptor*)adaptor;
ASSIGN(_serverSocket,stream);
_keepAlive=NO;
_maxSocketIdleTime=300; // 300 ms
_isMultiThreadEnabled = [adaptor isMultiThreadEnabled];
if (_isMultiThreadEnabled) {
_t = [[NSThread alloc] initWithTarget:self
selector:@selector(runOnce)
object:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector:@selector(threadWillExit:)
name:NSThreadWillExitNotification
object: _t];
_runFlag = YES;
[_t start];
} else {
_runFlag = YES;
[self runOnce];
}
}
return self;
}
- (void)threadWillExit:(NSNotification*)notification
{
NSLog(@"%s",__PRETTY_FUNCTION__);
[[NSNotificationCenter defaultCenter] removeObserver: self];
[_mtAdaptor workerThreadWillExit:self];
}
//PRIVATE!
- (void) _closeSocket
{
if (_currentSocket == nil) {
return;
}
[_currentSocket closeFile];
_currentSocket = nil;
}
-(void)runOnce
{
GSWRequest *request = nil;
BOOL ok = NO;
struct timeval timeout;
GSWResponse *response;
if ((!_runFlag) || (_serverSocket == nil)) {
return;
}
_pool = [[NSAutoreleasePool alloc] init];
_errorOnRead = NO;
// _maxSocketIdleTime is milisecs!
timeout.tv_sec = 0;
timeout.tv_usec = _maxSocketIdleTime * 1000;
NS_DURING {
setsockopt([_serverSocket fileDescriptor], SOL_SOCKET, SO_RCVTIMEO, &timeout,sizeof(timeout));
request = [GSWHTTPIO readRequestFromFromHandle: _serverSocket];
} NS_HANDLER {
_errorOnRead = YES;
NSLog(@"%s -- dropping connection reason: %@",__PRETTY_FUNCTION__, [localException reason]);
} NS_ENDHANDLER;
// "womp" is the request handler key used by the WOTaskD contacing your app
if ((_errorOnRead || (request == nil)) ||
((([[_app class] isDirectConnectEnabled] == NO) && ([request isUsingWebServer] == NO)) &&
([@"womp" isEqual:[request requestHandlerKey]] == NO))) {
goto done;
}
_processingRequest = YES;
_dispatchError = NO;
NS_DURING {
response = [_app dispatchRequest:request];
} NS_HANDLER {
NSLog(@"%s -- Exception occurred while responding to client: %@",
__PRETTY_FUNCTION__, [localException description]);
_dispatchError = YES;
response = [GSWDefaultAdaptor _lastDitchErrorResponse];
} NS_ENDHANDLER;
if (response) {
NSString * reqid = [request headerForKey:REQUEST_ID];
if (reqid) {
[response setHeader:reqid forKey:REQUEST_ID];
}
NS_DURING {
// request = [GSWHTTPIO readRequestFromFromHandle: _serverSocket];
ok = [GSWHTTPIO sendResponse:response
toHandle: _serverSocket
request:request];
} NS_HANDLER {
NSLog(@"%s -- Exception while sending response: %@",
__PRETTY_FUNCTION__, [localException description]);
} NS_ENDHANDLER;
}
done:
[self _closeSocket];
_processingRequest = NO;
if (_isMultiThreadEnabled) {
[NSThread exit];
}
}
// -[WOWorkerThread runLoopOnce]
// -[WOWorkerThread runOnce]
// -[WOWorkerThread run]
// -[WOWorkerThread stop]
// -[WOWorkerThread(WOWkrObjRequestHandling) readLine:withLength:]
// -[WOWorkerThread(WOWkrObjRequestHandling) readBlob:withLength:]
// -[WOWorkerThread(WOWkrObjRequestHandling) readRequest]
// -[WOWorkerThread(WOWkrObjRequestHandling) sendResponse:]
- (NSString*) description
{
return [NSString stringWithFormat:@"<%s %p socket:%@ >",
object_get_class_name(self),
(void*)self, _serverSocket];
}
@end
@implementation GSWWorkerThread (WorkerThreadDepricated)
-(id)initWithApp:(GSWApplication*)application
withAdaptor:(GSWAdaptor*)adaptor
withStream:(NSFileHandle*)stream
{
NSLog(@"%s is depricated use initWithApp:adaptor:stream: instead.",__PRETTY_FUNCTION__);
return [self initWithApp: application adaptor:adaptor stream:stream];
}
// this exists in WO 4.5
- (id) initWithApp:(GSWApplication*) app andMtAdaptor:(GSWAdaptor*) adaptor restricted:(void*) rst
{
return [self initWithApp:app adaptor:adaptor stream:rst];
}
@end

View file

@ -111,7 +111,6 @@
@class GSWStaticResourceRequestHandler;
@class GSWActionRequestHandler;
@class GSWDirectActionRequestHandler;
@class GSWDefaultAdaptorThread;
@class GSWKeyValueAssociation;
@class GSWConstantValueAssociation;
@class GSWHTMLStaticElement;
@ -235,7 +234,6 @@
#include "GSWStaticResourceRequestHandler.h"
#include "GSWActionRequestHandler.h"
#include "GSWDirectActionRequestHandler.h"
#include "GSWDefaultAdaptorThread.h"
#include "GSWKeyValueAssociation.h"
#include "GSWConstantValueAssociation.h"
#include "GSWTemplateParser.h"