mirror of
https://github.com/gnustep/libs-gsweb.git
synced 2025-02-24 04:01:16 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gsweb/trunk@25288 72102866-910b-0410-8b05-ffd578937521
884 lines
28 KiB
Objective-C
884 lines
28 KiB
Objective-C
/** 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
|
|
|