mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 00:41:02 +00:00
change to use NSFileHandle instead of streams
This commit is contained in:
parent
aae267ab13
commit
a426fab700
2 changed files with 287 additions and 420 deletions
|
@ -1,6 +1,6 @@
|
|||
/** -*- objc -*-
|
||||
*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
* Author: Sergei Golovin <svgdev@mail.ru>
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -28,8 +28,14 @@
|
|||
*/
|
||||
@interface SimpleWebServer : NSObject
|
||||
{
|
||||
/* holds the GSServerStream accepting incoming connections */
|
||||
GSServerStream *_serverStream;
|
||||
/* holds the file handler of connection */
|
||||
NSFileHandle *_fh;
|
||||
/* holds the 'near' file handler of connection...
|
||||
see "Background Inter-Process Communication Using Sockets"
|
||||
of Low-Level File Management Programming Topics
|
||||
*/
|
||||
NSFileHandle *_cfh;
|
||||
|
||||
/* the delegate ... NOT RETAINED...
|
||||
* see below the protocol SimpleWebServerDelegate */
|
||||
id _delegate;
|
||||
|
@ -41,36 +47,18 @@
|
|||
NSString *_port;
|
||||
/* SSL configuration and options */
|
||||
NSDictionary *_secure;
|
||||
|
||||
/* The following web-server code is derived/stolen from NSURL/Helpers/capture.m */
|
||||
|
||||
/* the stream to send */
|
||||
NSOutputStream *_op;
|
||||
/* the stream to receive */
|
||||
NSInputStream *_ip;
|
||||
/* the collector of received bytes from a client */
|
||||
NSMutableData *_capture;
|
||||
/* the number of sent bytes to a client */
|
||||
unsigned _written;
|
||||
/* the flag indicating the instance is collecting bytes from a client */
|
||||
BOOL _readable;
|
||||
/* the flag indicating the instance is sending bytes to a client */
|
||||
BOOL _writable;
|
||||
/* whether to use a secure TLS/SSL connection */
|
||||
BOOL _isSecure;
|
||||
/* the request is read */
|
||||
BOOL _doRespond;
|
||||
/* the response is written */
|
||||
BOOL _done;
|
||||
/* end of the stolen */
|
||||
|
||||
/* wether the output stream is ready to write */
|
||||
BOOL _canRespond;
|
||||
|
||||
/* the collector of received bytes from a client */
|
||||
NSMutableData *_capture;
|
||||
/* holds the current request */
|
||||
GSMimeDocument *_request;
|
||||
/* holds the current response */
|
||||
GSMimeDocument *_response;
|
||||
/* the flag the server wants to operate */
|
||||
BOOL _isRunning;
|
||||
/* to close the connection after sending the response */
|
||||
BOOL _isClose;
|
||||
}
|
||||
- (void)dealloc;
|
||||
|
||||
|
@ -111,9 +99,6 @@
|
|||
*/
|
||||
- (void)stop;
|
||||
|
||||
/* The method is derived/stolen from NSURL/Helpers/capture.m */
|
||||
- (void) stream: (NSStream *)theStream handleEvent: (NSStreamEvent)streamEvent;
|
||||
|
||||
@end /* SimpleWebServer */
|
||||
|
||||
@protocol SimpleWebServerDelegate
|
||||
|
|
|
@ -1,39 +1,27 @@
|
|||
/*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
* Author: Sergei Golovin <svgdev@mail.ru>
|
||||
*/
|
||||
|
||||
#import "SimpleWebServer.h"
|
||||
|
||||
/* the time step for the runloop */
|
||||
#define TIMING 0.1
|
||||
/* the time step for the runloop */
|
||||
#define TIMING 0.1
|
||||
|
||||
@interface SimpleWebServer (Private)
|
||||
/**
|
||||
* Starts a listening (server) stream.
|
||||
* Starts listening. Returns NO if the instance can't listen.
|
||||
*/
|
||||
- (void)_openServerStream;
|
||||
- (BOOL) _startListening;
|
||||
|
||||
/**
|
||||
* Stops the listening (server) stream.
|
||||
* Receives NSFileHandleConnectionAcceptedNotification.
|
||||
*/
|
||||
- (void)_closeServerStream;
|
||||
- (void) _accept:(NSNotification *)ntf;
|
||||
|
||||
/**
|
||||
* Reset to prepare for the next request-response cycle.
|
||||
* Receives NSFileHandleReadCompletionNotification.
|
||||
*/
|
||||
- (void)_resetCycle;
|
||||
|
||||
/**
|
||||
* Opens the input _ip and output _op streams, schedules them
|
||||
* on the current runloop. Requires the _ip and _op are not nil.
|
||||
*/
|
||||
- (void)_openIOStreams;
|
||||
|
||||
/**
|
||||
* Closes the input _ip and output _op streams, unschedules them
|
||||
* on the current runloop and clears that ivars.
|
||||
*/
|
||||
- (void)_closeIOStreams;
|
||||
- (void) _read:(NSNotification *)ntf;
|
||||
|
||||
/**
|
||||
* Tries to recognise if the request's bytes have been read (HTTP message's
|
||||
|
@ -42,18 +30,34 @@
|
|||
*/
|
||||
- (BOOL)_tryCaptured;
|
||||
|
||||
/**
|
||||
* Makes and sends response.
|
||||
*/
|
||||
- (void) _makeAndSendResponse;
|
||||
|
||||
/**
|
||||
* Reset to prepare for the next request-response cycle.
|
||||
*/
|
||||
- (void)_resetCycle;
|
||||
|
||||
/**
|
||||
* Closes IO
|
||||
*/
|
||||
- (void) _close;
|
||||
|
||||
@end /* SimpleWebServer (Private) */
|
||||
|
||||
@implementation SimpleWebServer
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
||||
_delegate = nil;
|
||||
DESTROY(_capture);
|
||||
DESTROY(_request);
|
||||
DESTROY(_response);
|
||||
[self _closeIOStreams];
|
||||
[self _closeServerStream];
|
||||
DESTROY(_cfh);
|
||||
DESTROY(_fh);
|
||||
DESTROY(_address);
|
||||
DESTROY(_port);
|
||||
DESTROY(_secure);
|
||||
|
@ -66,11 +70,9 @@
|
|||
{
|
||||
_debug = NO;
|
||||
_delegate = nil;
|
||||
_doRespond = NO;
|
||||
_done = NO;
|
||||
_canRespond = NO;
|
||||
_ip = nil;
|
||||
_op = nil;
|
||||
_isSecure = NO;
|
||||
_isRunning = NO;
|
||||
_isClose = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -79,13 +81,12 @@
|
|||
/* getters */
|
||||
- (NSString *)port
|
||||
{
|
||||
NSStream *s = _serverStream;
|
||||
if (nil !=_fh)
|
||||
{
|
||||
return _port;
|
||||
}
|
||||
|
||||
if (nil == s) s = _ip;
|
||||
if (nil == s) s = _op;
|
||||
if (nil == s) return nil;
|
||||
|
||||
return [s streamStatus] == NSStreamStatusOpen ? _port : nil;
|
||||
return nil;
|
||||
}
|
||||
/* end of getters */
|
||||
|
||||
|
@ -94,26 +95,11 @@
|
|||
port:(NSString *)port
|
||||
secure:(NSDictionary *)dict
|
||||
{
|
||||
BOOL ret = NO;
|
||||
ASSIGN(_address, address);
|
||||
ASSIGN(_port, port);
|
||||
ASSIGN(_secure, dict);
|
||||
|
||||
if (nil == _serverStream)
|
||||
{
|
||||
ASSIGN(_address, address);
|
||||
ASSIGN(_port, port);
|
||||
ASSIGN(_secure, dict);
|
||||
[self _openServerStream];
|
||||
if (nil != _serverStream)
|
||||
{
|
||||
ret = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"%@: already started '%@' on '%@'",
|
||||
self, _serverStream, [NSThread currentThread]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return [self _startListening];
|
||||
}
|
||||
|
||||
- (void)setDebug:(BOOL)flag
|
||||
|
@ -130,353 +116,146 @@
|
|||
|
||||
- (void)stop
|
||||
{
|
||||
NSRunLoop *rl = [NSRunLoop currentRunLoop];;
|
||||
|
||||
if (nil != _serverStream)
|
||||
{
|
||||
[self _closeServerStream];
|
||||
DESTROY(_address);
|
||||
DESTROY(_port);
|
||||
DESTROY(_secure);
|
||||
}
|
||||
|
||||
[self _closeIOStreams];
|
||||
|
||||
// give a time slice to free all resources
|
||||
[rl runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self
|
||||
name: NSFileHandleConnectionAcceptedNotification
|
||||
object: _fh];
|
||||
[self _close];
|
||||
DESTROY(_fh);
|
||||
DESTROY(_address);
|
||||
DESTROY(_port);
|
||||
DESTROY(_secure);
|
||||
_isRunning = NO;
|
||||
}
|
||||
|
||||
/* GSStream's delegate */
|
||||
|
||||
/* The method is derived/stolen from NSURL/Helpers/capture.m */
|
||||
- (void) stream: (NSStream *)theStream handleEvent: (NSStreamEvent)streamEvent
|
||||
{
|
||||
NSRunLoop *rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
// NSLog(@"Event %p %d", theStream, streamEvent);
|
||||
|
||||
switch (streamEvent)
|
||||
{
|
||||
case NSStreamEventHasBytesAvailable:
|
||||
{
|
||||
if (_ip == nil)
|
||||
{
|
||||
[self _resetCycle];
|
||||
[(GSServerStream*)_serverStream acceptWithInputStream: &_ip
|
||||
outputStream: &_op];
|
||||
if (_ip) // it is ok to accept nothing
|
||||
{
|
||||
RETAIN(_ip);
|
||||
RETAIN(_op);
|
||||
[self _openIOStreams];
|
||||
[self _closeServerStream];
|
||||
}
|
||||
}
|
||||
if (theStream == _ip)
|
||||
{
|
||||
_readable = YES;
|
||||
while (_readable == YES)
|
||||
{
|
||||
unsigned char buffer[BUFSIZ];
|
||||
int readSize;
|
||||
|
||||
readSize = [_ip read: buffer maxLength: sizeof(buffer)];
|
||||
if (readSize <= 0)
|
||||
{
|
||||
_readable = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_capture appendBytes: buffer length: readSize];
|
||||
}
|
||||
}
|
||||
|
||||
_doRespond = [self _tryCaptured];
|
||||
if (_doRespond)
|
||||
{
|
||||
// reset the output stream to trigger polling
|
||||
[_op write: NULL maxLength: 0];
|
||||
if (YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: about to send response\n%@", self, _response);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
{
|
||||
if (_doRespond && _canRespond)
|
||||
{
|
||||
NSMutableData *data;
|
||||
NSString *status;
|
||||
NSData *statusData;
|
||||
char *crlf = "\r\n";
|
||||
id content;
|
||||
NSData *contentData = nil;
|
||||
NSUInteger cLength = 0; // content-length
|
||||
NSString *connection;
|
||||
BOOL close = YES;
|
||||
|
||||
NSAssert(theStream == _op, @"Wrong stream for writing");
|
||||
_writable = YES;
|
||||
|
||||
// adding the 'Connection' to the response
|
||||
connection = [[_request headerNamed: @"Connection"] value];
|
||||
if (nil == connection)
|
||||
{
|
||||
connection = [[_request headerNamed: @"connection"] value];
|
||||
}
|
||||
// if the client didn't supply the header 'Connection' or
|
||||
// explicitly stated to close the current connection
|
||||
close = (nil == connection
|
||||
|| [[connection lowercaseString] isEqualToString: @"close"]);
|
||||
|
||||
// adding the 'Content-Length' to the response
|
||||
content = [_response content];
|
||||
if ([content isKindOfClass: [NSString class]])
|
||||
{
|
||||
contentData = [(NSString *)content
|
||||
dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
else if ([content isKindOfClass: [NSData class]])
|
||||
{
|
||||
contentData = (NSData *)content;
|
||||
}
|
||||
else
|
||||
{
|
||||
// yet unsupported
|
||||
}
|
||||
if (nil != content)
|
||||
{
|
||||
cLength = [contentData length];
|
||||
if (cLength > 0)
|
||||
{
|
||||
NSString *l;
|
||||
|
||||
l = [NSString stringWithFormat: @"%u", (unsigned)cLength];
|
||||
[_response setHeader: @"Content-Length"
|
||||
value: l
|
||||
parameters: nil];
|
||||
}
|
||||
}
|
||||
if (cLength == 0)
|
||||
{
|
||||
[_response setHeader: @"Content-Length"
|
||||
value: @"0"
|
||||
parameters: nil];
|
||||
}
|
||||
|
||||
// adding the status line
|
||||
status = [[_response headerNamed: @"http"] value];
|
||||
if (nil == status)
|
||||
{
|
||||
status = [[_response headerNamed: @"HTTP"] value];
|
||||
}
|
||||
if (nil == status)
|
||||
{
|
||||
status = [[_response headerNamed: @"Http"] value];
|
||||
}
|
||||
statusData = [status dataUsingEncoding: NSUTF8StringEncoding];
|
||||
data = [NSMutableData dataWithData: statusData];
|
||||
[_response deleteHeaderNamed: @"http"];
|
||||
[data appendBytes: crlf length: 2];
|
||||
|
||||
// actual sending
|
||||
[data appendData: [_response rawMimeData]];
|
||||
while (_writable == YES && _written < [data length])
|
||||
{
|
||||
int result = [_op write: [data bytes] + _written
|
||||
maxLength: [data length] - _written];
|
||||
|
||||
if (result <= 0)
|
||||
{
|
||||
_writable = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
_written += result;
|
||||
}
|
||||
}
|
||||
if (_written == [data length])
|
||||
{
|
||||
if (close)
|
||||
{
|
||||
// if the client didn't supply the header 'Connection' or explicitly stated
|
||||
// to close the current connection
|
||||
[self _closeIOStreams];
|
||||
[self _openServerStream];
|
||||
}
|
||||
// ready for another request-response cycle
|
||||
[self _resetCycle];
|
||||
}
|
||||
}
|
||||
|
||||
_canRespond = YES;
|
||||
|
||||
break;
|
||||
}
|
||||
case NSStreamEventEndEncountered:
|
||||
{
|
||||
if (theStream == _ip || theStream == _op)
|
||||
{
|
||||
[self _closeIOStreams];
|
||||
[self _resetCycle];
|
||||
[self _openServerStream];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theStream close];
|
||||
[theStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NSStreamEventErrorOccurred:
|
||||
{
|
||||
int code = [[theStream streamError] code];
|
||||
|
||||
if (theStream == _ip || theStream == _op)
|
||||
{
|
||||
[self _closeIOStreams];
|
||||
[self _resetCycle];
|
||||
[self _openServerStream];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theStream close];
|
||||
[theStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
NSAssert1(1, @"Error! code is %d", code);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* end of GSStream's delegate */
|
||||
|
||||
@end /* SimpleWebServer */
|
||||
|
||||
@implementation SimpleWebServer (Private)
|
||||
|
||||
- (void)_openServerStream
|
||||
- (BOOL) _startListening
|
||||
{
|
||||
NSString *certFile = nil;
|
||||
NSString *keyFile = nil;
|
||||
NSRunLoop *rl;
|
||||
|
||||
if (nil == _serverStream)
|
||||
/*NSDictionary *sslOptions = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"CertificateFile.pem", GSTLSCertificateFile,
|
||||
@"KeyFile.pem", GSTLSCertificateKeyFile,
|
||||
nil];*/
|
||||
if (nil != _secure
|
||||
&& [_secure objectForKey: @"CertificateFile"] != nil
|
||||
&& [_secure objectForKey: @"KeyFile"] != nil)
|
||||
{
|
||||
rl = [NSRunLoop currentRunLoop];
|
||||
Class sslClass = [NSFileHandle sslClass];
|
||||
|
||||
_serverStream = [GSServerStream serverStreamToAddr: _address port: [_port intValue]];
|
||||
RETAIN(_serverStream);
|
||||
if (_serverStream == nil)
|
||||
_isSecure = YES;
|
||||
_fh = [sslClass fileHandleAsServerAtAddress: _address
|
||||
service: _port
|
||||
protocol: @"tcp"];
|
||||
[_fh sslSetOptions: _secure];
|
||||
}
|
||||
else
|
||||
{
|
||||
_fh = [NSFileHandle fileHandleAsServerAtAddress: _address
|
||||
service: _port
|
||||
protocol: @"tcp"];
|
||||
}
|
||||
RETAIN(_fh);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(_accept:)
|
||||
name: NSFileHandleConnectionAcceptedNotification
|
||||
object: _fh];
|
||||
|
||||
[_fh acceptConnectionInBackgroundAndNotify];
|
||||
_isRunning = YES;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) _accept:(NSNotification *)ntf
|
||||
{
|
||||
|
||||
if (_isRunning)
|
||||
{
|
||||
NSDictionary *info = [ntf userInfo];
|
||||
NSFileHandle *fh = [info objectForKey: NSFileHandleNotificationFileHandleItem];
|
||||
|
||||
if (_cfh != nil)
|
||||
{
|
||||
NSLog(@"Failed to create server stream (address: %@, port: %@)", _address, _port);
|
||||
[self _close];
|
||||
}
|
||||
|
||||
ASSIGN(_cfh, fh);
|
||||
|
||||
[_fh acceptConnectionInBackgroundAndNotify];
|
||||
if (_isSecure && [_cfh sslAccept])
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(_read:)
|
||||
name: NSFileHandleReadCompletionNotification
|
||||
object: _cfh];
|
||||
|
||||
[_cfh readInBackgroundAndNotify];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(_read:)
|
||||
name: NSFileHandleReadCompletionNotification
|
||||
object: _cfh];
|
||||
[_cfh readInBackgroundAndNotify];
|
||||
}
|
||||
NSLog(@"accept");
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _read:(NSNotification *)ntf
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
NSDictionary *info = [ntf userInfo];
|
||||
|
||||
if([info objectForKey: GSFileHandleNotificationError])
|
||||
{
|
||||
[self _close];
|
||||
return;
|
||||
}
|
||||
|
||||
if (nil != _secure &&
|
||||
(certFile = [_secure objectForKey: @"CertificateFile"]) != nil &&
|
||||
(keyFile = [_secure objectForKey: @"KeyFile"]) != nil)
|
||||
if([[ntf name] isEqual: NSFileHandleReadCompletionNotification])
|
||||
{
|
||||
_isSecure = YES;
|
||||
[_serverStream setProperty: NSStreamSocketSecurityLevelTLSv1 forKey: NSStreamSocketSecurityLevelKey];
|
||||
[_serverStream setProperty: certFile forKey: GSTLSCertificateFile];
|
||||
[_serverStream setProperty: keyFile forKey: GSTLSCertificateKeyFile];
|
||||
}
|
||||
NSData *hunk = [info objectForKey: NSFileHandleNotificationDataItem];
|
||||
if (nil != hunk && [hunk length] > 0)
|
||||
{
|
||||
if (nil == _capture)
|
||||
{
|
||||
_capture = [NSMutableData new];
|
||||
}
|
||||
[_capture appendData: hunk];
|
||||
if ([self _tryCaptured]) // <- the _request and _response are allocated
|
||||
{
|
||||
NSLog(@"make and send");
|
||||
[self _makeAndSendResponse];
|
||||
// ready for another request-response cycle
|
||||
[self _resetCycle]; // <- the _request and _response are deallocated
|
||||
|
||||
_capture = [NSMutableData new];
|
||||
|
||||
[_serverStream setDelegate: self];
|
||||
[_serverStream scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
[_serverStream open];
|
||||
if (YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: started '%@' on '%@'", self, _serverStream, [NSThread currentThread]);
|
||||
if (_isClose)
|
||||
{
|
||||
// if the client didn't supply the header 'Connection' or explicitly stated
|
||||
// to close the current connection
|
||||
//[_cfh readInBackgroundAndNotify];
|
||||
[self _close];
|
||||
return;
|
||||
// BEWARE: it can left the socket busy for HTTP after server stopping (for HTTPS is OK)
|
||||
// so consequent tests are failed bc their server can't bind
|
||||
}
|
||||
}
|
||||
[_cfh readInBackgroundAndNotify];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _close];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"%@: already started '%@' on '%@'", self, _serverStream, [NSThread currentThread]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_closeServerStream
|
||||
{
|
||||
NSRunLoop *rl;
|
||||
|
||||
if (nil != _serverStream)
|
||||
{
|
||||
rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
[_serverStream close];
|
||||
[_serverStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
if (YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: stopped server stream %@", self, _serverStream);
|
||||
}
|
||||
DESTROY(_serverStream);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_resetCycle
|
||||
{
|
||||
_written = 0;
|
||||
_doRespond = NO;
|
||||
_done = NO;
|
||||
_canRespond = NO;
|
||||
[_capture setLength: 0];
|
||||
}
|
||||
|
||||
- (void)_openIOStreams
|
||||
{
|
||||
NSRunLoop *rl;
|
||||
|
||||
if (_ip != nil && _op != nil)
|
||||
{
|
||||
rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
[_ip scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
[_op scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
[_ip setDelegate: self];
|
||||
[_op setDelegate: self];
|
||||
[_ip open];
|
||||
[_op open];
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"%@: IO streams not properly initialized", self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_closeIOStreams
|
||||
{
|
||||
NSRunLoop *rl;
|
||||
|
||||
if (nil != _ip || nil != _op)
|
||||
{
|
||||
rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
[_ip close];
|
||||
[_ip removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
|
||||
[_op close];
|
||||
[_op removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
|
||||
DESTROY(_ip);
|
||||
DESTROY(_op);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)_tryCaptured
|
||||
- (BOOL) _tryCaptured
|
||||
{
|
||||
BOOL ret = NO;
|
||||
NSRange r1;
|
||||
|
@ -490,7 +269,7 @@
|
|||
// when all request's bytes are read... it waits for full headers and
|
||||
// reads the Content-Length's value then waits for the number of bytes
|
||||
// equal to that value is read
|
||||
tmp1 = [[NSString alloc] initWithData: _capture
|
||||
tmp1 = [[NSString alloc] initWithData: _capture
|
||||
encoding: NSUTF8StringEncoding];
|
||||
// whether the headers are read
|
||||
if ((r1 = [tmp1 rangeOfString: @"\r\n\r\n"]).location != NSNotFound)
|
||||
|
@ -521,7 +300,7 @@
|
|||
NSData *data;
|
||||
|
||||
// TODO: currently no checks
|
||||
r2 = [headers rangeOfString: @"\r\n"];
|
||||
r2 = [headers rangeOfString: @"\r\n"];
|
||||
while (r2.location == 0)
|
||||
{
|
||||
// ignore an empty line before the request line
|
||||
|
@ -539,11 +318,11 @@
|
|||
tmp2 = [tmp2 stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
|
||||
|
||||
// find the method
|
||||
r2 = [tmp2 rangeOfString: @" "];
|
||||
r2 = [tmp2 rangeOfString: @" "];
|
||||
method = [[tmp2 substringToIndex: r2.location] uppercaseString];
|
||||
tmp2 = [[tmp2 substringFromIndex: r2.location + 1]
|
||||
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
|
||||
|
||||
|
||||
r2 = [tmp2 rangeOfString: @"?"]; // path?query
|
||||
if (r2.location != NSNotFound)
|
||||
{
|
||||
|
@ -568,15 +347,15 @@
|
|||
[_request setHeader: @"x-http-method"
|
||||
value: method
|
||||
parameters: nil];
|
||||
|
||||
|
||||
[_request setHeader: @"x-http-path"
|
||||
value: path
|
||||
parameters: nil];
|
||||
|
||||
|
||||
[_request setHeader: @"x-http-query"
|
||||
value: query
|
||||
parameters: nil];
|
||||
|
||||
|
||||
[_request setHeader: @"x-http-scheme"
|
||||
value: scheme
|
||||
parameters: nil];
|
||||
|
@ -584,7 +363,7 @@
|
|||
[_request setHeader: @"x-http-version"
|
||||
value: version
|
||||
parameters: nil];
|
||||
|
||||
|
||||
if (YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: got request\n%@", self, _request);
|
||||
|
@ -609,4 +388,107 @@
|
|||
return ret;
|
||||
}
|
||||
|
||||
- (void) _makeAndSendResponse
|
||||
{
|
||||
NSMutableData *data;
|
||||
NSString *status;
|
||||
NSData *statusData;
|
||||
char *crlf = "\r\n";
|
||||
id content;
|
||||
NSData *contentData = nil;
|
||||
NSUInteger cLength = 0; // content-length
|
||||
NSString *connection;
|
||||
|
||||
// adding the 'Connection' to the response
|
||||
connection = [[_request headerNamed: @"Connection"] value];
|
||||
if (nil == connection)
|
||||
{
|
||||
connection = [[_request headerNamed: @"connection"] value];
|
||||
}
|
||||
// if the client didn't supply the header 'Connection' or
|
||||
// explicitly stated to close the current connection
|
||||
_isClose = (nil == connection ||
|
||||
[[connection lowercaseString] isEqualToString: @"close"]);
|
||||
|
||||
// adding the 'Content-Length' to the response
|
||||
content = [_response content];
|
||||
if ([content isKindOfClass: [NSString class]])
|
||||
{
|
||||
contentData = [(NSString *)content
|
||||
dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
else if ([content isKindOfClass: [NSData class]])
|
||||
{
|
||||
contentData = (NSData *)content;
|
||||
}
|
||||
else
|
||||
{
|
||||
// yet unsupported
|
||||
}
|
||||
if (nil != content)
|
||||
{
|
||||
cLength = [contentData length];
|
||||
if (cLength > 0)
|
||||
{
|
||||
NSString *l;
|
||||
|
||||
l = [NSString stringWithFormat: @"%u", (unsigned)cLength];
|
||||
[_response setHeader: @"Content-Length"
|
||||
value: l
|
||||
parameters: nil];
|
||||
}
|
||||
}
|
||||
if (cLength == 0)
|
||||
{
|
||||
[_response setHeader: @"Content-Length"
|
||||
value: @"0"
|
||||
parameters: nil];
|
||||
}
|
||||
|
||||
// adding the status line
|
||||
status = [[_response headerNamed: @"http"] value];
|
||||
if (nil == status)
|
||||
{
|
||||
status = [[_response headerNamed: @"HTTP"] value];
|
||||
}
|
||||
if (nil == status)
|
||||
{
|
||||
status = [[_response headerNamed: @"Http"] value];
|
||||
}
|
||||
statusData = [status dataUsingEncoding: NSUTF8StringEncoding];
|
||||
data = [NSMutableData dataWithData: statusData];
|
||||
[_response deleteHeaderNamed: @"http"];
|
||||
[data appendBytes: crlf length: 2];
|
||||
|
||||
// actual sending
|
||||
[data appendData: [_response rawMimeData]];
|
||||
|
||||
if (YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: about to send response\n%@", self, _response);
|
||||
}
|
||||
|
||||
[_cfh writeData: data];
|
||||
}
|
||||
|
||||
- (void)_resetCycle
|
||||
{
|
||||
DESTROY(_response);
|
||||
DESTROY(_request);
|
||||
DESTROY(_capture);
|
||||
}
|
||||
|
||||
- (void) _close
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self
|
||||
name: NSFileHandleReadCompletionNotification
|
||||
object: _cfh];
|
||||
if(_isSecure)
|
||||
{
|
||||
[_cfh sslDisconnect];
|
||||
}
|
||||
DESTROY(_cfh);
|
||||
NSLog(@"close");
|
||||
}
|
||||
|
||||
@end /* SimpleWebServer (Private) */
|
||||
|
|
Loading…
Reference in a new issue