2014-11-29 11:39:38 +00:00
|
|
|
/*
|
2024-03-06 10:12:30 +00:00
|
|
|
* Author: Sergei Golovin <svgdev@mail.ru>
|
2014-11-29 11:39:38 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#import "SimpleWebServer.h"
|
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
/* the time step for the runloop */
|
|
|
|
#define TIMING 0.1
|
2014-11-29 11:39:38 +00:00
|
|
|
|
|
|
|
@interface SimpleWebServer (Private)
|
|
|
|
/**
|
2024-03-06 10:12:30 +00:00
|
|
|
* Starts listening. Returns NO if the instance can't listen.
|
2014-11-29 11:39:38 +00:00
|
|
|
*/
|
2024-03-06 10:12:30 +00:00
|
|
|
- (BOOL) _startListening;
|
2014-11-29 11:39:38 +00:00
|
|
|
|
|
|
|
/**
|
2024-03-06 10:12:30 +00:00
|
|
|
* Receives NSFileHandleConnectionAcceptedNotification.
|
2014-11-29 11:39:38 +00:00
|
|
|
*/
|
2024-03-06 10:12:30 +00:00
|
|
|
- (void) _accept:(NSNotification *)ntf;
|
2014-11-29 11:39:38 +00:00
|
|
|
|
|
|
|
/**
|
2024-03-06 10:12:30 +00:00
|
|
|
* Receives NSFileHandleReadCompletionNotification.
|
2014-11-29 11:39:38 +00:00
|
|
|
*/
|
2024-03-06 10:12:30 +00:00
|
|
|
- (void) _read:(NSNotification *)ntf;
|
2014-11-29 11:39:38 +00:00
|
|
|
|
|
|
|
/**
|
2024-03-06 10:12:30 +00:00
|
|
|
* Tries to recognise if the request's bytes have been read (HTTP message's
|
|
|
|
* headers and body) and if so then it proceeds the request and produces
|
|
|
|
* a response. If the response is ready it returns YES.
|
2014-11-29 11:39:38 +00:00
|
|
|
*/
|
2024-03-06 10:12:30 +00:00
|
|
|
- (BOOL)_tryCaptured;
|
2014-11-29 11:39:38 +00:00
|
|
|
|
|
|
|
/**
|
2024-03-06 10:12:30 +00:00
|
|
|
* Makes and sends response.
|
2014-11-29 11:39:38 +00:00
|
|
|
*/
|
2024-03-06 10:12:30 +00:00
|
|
|
- (void) _makeAndSendResponse;
|
2014-11-29 11:39:38 +00:00
|
|
|
|
|
|
|
/**
|
2024-03-06 10:12:30 +00:00
|
|
|
* Reset to prepare for the next request-response cycle.
|
2014-11-29 11:39:38 +00:00
|
|
|
*/
|
2024-03-06 10:12:30 +00:00
|
|
|
- (void)_resetCycle;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closes IO
|
|
|
|
*/
|
|
|
|
- (void) _close;
|
2014-11-29 11:39:38 +00:00
|
|
|
|
|
|
|
@end /* SimpleWebServer (Private) */
|
|
|
|
|
|
|
|
@implementation SimpleWebServer
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
2014-11-29 11:39:38 +00:00
|
|
|
_delegate = nil;
|
|
|
|
DESTROY(_capture);
|
|
|
|
DESTROY(_request);
|
|
|
|
DESTROY(_response);
|
2024-03-06 10:12:30 +00:00
|
|
|
DESTROY(_cfh);
|
|
|
|
DESTROY(_fh);
|
2014-11-29 11:39:38 +00:00
|
|
|
DESTROY(_address);
|
|
|
|
DESTROY(_port);
|
|
|
|
DESTROY(_secure);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)init
|
|
|
|
{
|
2014-11-29 11:55:16 +00:00
|
|
|
if ((self = [super init]) != nil)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
_debug = NO;
|
|
|
|
_delegate = nil;
|
2024-03-06 10:12:30 +00:00
|
|
|
_isSecure = NO;
|
|
|
|
_isRunning = NO;
|
|
|
|
_isClose = NO;
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* getters */
|
|
|
|
- (NSString *)port
|
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
if (nil !=_fh)
|
|
|
|
{
|
|
|
|
return _port;
|
|
|
|
}
|
2014-11-29 11:39:38 +00:00
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
return nil;
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
/* end of getters */
|
|
|
|
|
|
|
|
/* setters */
|
|
|
|
- (BOOL)setAddress:(NSString *)address
|
|
|
|
port:(NSString *)port
|
|
|
|
secure:(NSDictionary *)dict
|
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
ASSIGN(_address, address);
|
|
|
|
ASSIGN(_port, port);
|
|
|
|
ASSIGN(_secure, dict);
|
2014-11-29 11:39:38 +00:00
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
return [self _startListening];
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setDebug:(BOOL)flag
|
|
|
|
{
|
|
|
|
_debug = flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setDelegate:(id)delegate
|
|
|
|
{
|
|
|
|
_delegate = delegate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* end of setters */
|
|
|
|
|
|
|
|
- (void)stop
|
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self
|
|
|
|
name: NSFileHandleConnectionAcceptedNotification
|
|
|
|
object: _fh];
|
|
|
|
[self _close];
|
|
|
|
DESTROY(_fh);
|
|
|
|
DESTROY(_address);
|
|
|
|
DESTROY(_port);
|
|
|
|
DESTROY(_secure);
|
|
|
|
_isRunning = NO;
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end /* SimpleWebServer */
|
|
|
|
|
|
|
|
@implementation SimpleWebServer (Private)
|
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
- (BOOL) _startListening
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
if (nil != _secure
|
|
|
|
&& [_secure objectForKey: @"CertificateFile"] != nil
|
|
|
|
&& [_secure objectForKey: @"KeyFile"] != nil)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
Class sslClass = [NSFileHandle sslClass];
|
2014-11-29 11:39:38 +00:00
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
_isSecure = YES;
|
|
|
|
_fh = [sslClass fileHandleAsServerAtAddress: _address
|
|
|
|
service: _port
|
|
|
|
protocol: @"tcp"];
|
|
|
|
[_fh sslSetOptions: _secure];
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
_fh = [NSFileHandle fileHandleAsServerAtAddress: _address
|
|
|
|
service: _port
|
|
|
|
protocol: @"tcp"];
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
2024-03-06 10:12:30 +00:00
|
|
|
RETAIN(_fh);
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver: self
|
|
|
|
selector: @selector(_accept:)
|
|
|
|
name: NSFileHandleConnectionAcceptedNotification
|
|
|
|
object: _fh];
|
|
|
|
|
|
|
|
[_fh acceptConnectionInBackgroundAndNotify];
|
|
|
|
_isRunning = YES;
|
|
|
|
|
|
|
|
return YES;
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
- (void) _accept:(NSNotification *)ntf
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
if (_isRunning)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
NSDictionary *info = [ntf userInfo];
|
|
|
|
NSFileHandle *fh = [info objectForKey: NSFileHandleNotificationFileHandleItem];
|
2014-11-29 11:39:38 +00:00
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
if (_cfh != nil)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
[self _close];
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
ASSIGN(_cfh, fh);
|
2014-11-29 11:39:38 +00:00
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
[_fh acceptConnectionInBackgroundAndNotify];
|
2014-11-29 11:39:38 +00:00
|
|
|
|
2024-03-07 11:02:53 +00:00
|
|
|
if (!_isSecure || (_isSecure && [_cfh sslAccept]))
|
2024-03-06 10:12:30 +00:00
|
|
|
{
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver: self
|
|
|
|
selector: @selector(_read:)
|
|
|
|
name: NSFileHandleReadCompletionNotification
|
|
|
|
object: _cfh];
|
2024-03-07 11:02:53 +00:00
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
[_cfh readInBackgroundAndNotify];
|
|
|
|
}
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
- (void) _read:(NSNotification *)ntf
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
if (_isRunning)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
2024-03-06 10:12:30 +00:00
|
|
|
NSDictionary *info = [ntf userInfo];
|
2014-11-29 11:39:38 +00:00
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
if([info objectForKey: GSFileHandleNotificationError])
|
|
|
|
{
|
|
|
|
[self _close];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if([[ntf name] isEqual: NSFileHandleReadCompletionNotification])
|
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
|
|
|
[self _makeAndSendResponse];
|
|
|
|
// ready for another request-response cycle
|
|
|
|
[self _resetCycle]; // <- the _request and _response are deallocated
|
2014-11-29 11:39:38 +00:00
|
|
|
|
2024-03-07 11:17:00 +00:00
|
|
|
if (_isClose)
|
2024-03-06 10:12:30 +00:00
|
|
|
{
|
|
|
|
// if the client didn't supply the header 'Connection' or explicitly stated
|
|
|
|
// to close the current connection
|
|
|
|
[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
|
2024-03-07 11:17:00 +00:00
|
|
|
}
|
2024-03-06 10:12:30 +00:00
|
|
|
}
|
|
|
|
[_cfh readInBackgroundAndNotify];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self _close];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2014-11-29 11:39:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
- (BOOL) _tryCaptured
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
BOOL ret = NO;
|
|
|
|
NSRange r1;
|
|
|
|
NSRange r2;
|
|
|
|
NSString *headers;
|
|
|
|
NSString *tmp1;
|
|
|
|
NSString *tmp2;
|
2014-11-29 11:55:16 +00:00
|
|
|
NSUInteger contentLength = 0;
|
2014-11-29 11:39:38 +00:00
|
|
|
|
|
|
|
// the following chunk ensures that the captured data are written only
|
|
|
|
// 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
|
2024-03-06 10:12:30 +00:00
|
|
|
tmp1 = [[NSString alloc] initWithData: _capture
|
2014-11-29 11:39:38 +00:00
|
|
|
encoding: NSUTF8StringEncoding];
|
|
|
|
// whether the headers are read
|
2014-11-29 11:55:16 +00:00
|
|
|
if ((r1 = [tmp1 rangeOfString: @"\r\n\r\n"]).location != NSNotFound)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
headers = [tmp1 substringToIndex: r1.location + 2];
|
2014-11-29 11:55:16 +00:00
|
|
|
if ((r2 = [[headers lowercaseString] rangeOfString: @"content-length:"]).location != NSNotFound)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
tmp2 = [headers substringFromIndex: r2.location + r2.length]; // content-length:<tmp2><end of headers>
|
2014-11-29 11:55:16 +00:00
|
|
|
if ((r2 = [tmp2 rangeOfString: @"\r\n"]).location != NSNotFound)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
// full line with content-length is present
|
|
|
|
tmp2 = [tmp2 substringToIndex: r2.location]; // number of content's bytes
|
|
|
|
contentLength = [tmp2 intValue];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
contentLength = 0; // no header 'content-length'
|
|
|
|
}
|
2014-11-29 11:55:16 +00:00
|
|
|
if (r1.location + 4 + contentLength == [_capture length]) // Did we get headers + body?
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
// The request has been received
|
|
|
|
NSString *method = @"";
|
|
|
|
NSString *query = @"";
|
|
|
|
NSString *version = @"";
|
|
|
|
NSString *scheme = _isSecure ? @"https" : @"http";
|
|
|
|
NSString *path = @"";
|
|
|
|
NSData *data;
|
|
|
|
|
|
|
|
// TODO: currently no checks
|
2024-03-06 10:12:30 +00:00
|
|
|
r2 = [headers rangeOfString: @"\r\n"];
|
2014-11-29 11:55:16 +00:00
|
|
|
while (r2.location == 0)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
// ignore an empty line before the request line
|
|
|
|
headers = [headers substringFromIndex: 2];
|
|
|
|
r2 = [headers rangeOfString: @"\r\n"];
|
|
|
|
}
|
|
|
|
// the request line has been caught
|
|
|
|
tmp2 = [tmp1 substringFromIndex: r2.location + 2]; // whole request without the first line
|
|
|
|
data = [tmp2 dataUsingEncoding: NSUTF8StringEncoding];
|
|
|
|
_request = [GSMimeParser documentFromData: data];
|
|
|
|
RETAIN(_request);
|
|
|
|
|
|
|
|
// x-http-...
|
|
|
|
tmp2 = [headers substringToIndex: r2.location]; // the request line
|
|
|
|
tmp2 = [tmp2 stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
|
|
|
|
|
|
|
|
// find the method
|
2024-03-06 10:12:30 +00:00
|
|
|
r2 = [tmp2 rangeOfString: @" "];
|
2014-11-29 11:39:38 +00:00
|
|
|
method = [[tmp2 substringToIndex: r2.location] uppercaseString];
|
|
|
|
tmp2 = [[tmp2 substringFromIndex: r2.location + 1]
|
|
|
|
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
|
2024-03-06 10:12:30 +00:00
|
|
|
|
2014-11-29 11:39:38 +00:00
|
|
|
r2 = [tmp2 rangeOfString: @"?"]; // path?query
|
2014-11-29 11:55:16 +00:00
|
|
|
if (r2.location != NSNotFound)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
// path?query
|
|
|
|
path = [tmp2 substringToIndex: r2.location];
|
|
|
|
tmp2 = [tmp2 substringFromIndex: r2.location + 1]; // without '?'
|
|
|
|
r2 = [tmp2 rangeOfString: @" "];
|
|
|
|
query = [tmp2 substringToIndex: r2.location];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// only path
|
|
|
|
r2 = [tmp2 rangeOfString: @" "];
|
|
|
|
path = [tmp2 substringToIndex: r2.location];
|
|
|
|
}
|
|
|
|
tmp2 = [[tmp2 substringFromIndex: r2.location + 1]
|
|
|
|
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
|
|
|
|
// tmp2 == 'HTTP/<version>'
|
|
|
|
version = [tmp2 substringFromIndex: 5];
|
|
|
|
|
|
|
|
|
|
|
|
[_request setHeader: @"x-http-method"
|
|
|
|
value: method
|
|
|
|
parameters: nil];
|
2024-03-06 10:12:30 +00:00
|
|
|
|
2014-11-29 11:39:38 +00:00
|
|
|
[_request setHeader: @"x-http-path"
|
|
|
|
value: path
|
|
|
|
parameters: nil];
|
2024-03-06 10:12:30 +00:00
|
|
|
|
2014-11-29 11:39:38 +00:00
|
|
|
[_request setHeader: @"x-http-query"
|
|
|
|
value: query
|
|
|
|
parameters: nil];
|
2024-03-06 10:12:30 +00:00
|
|
|
|
2014-11-29 11:39:38 +00:00
|
|
|
[_request setHeader: @"x-http-scheme"
|
|
|
|
value: scheme
|
|
|
|
parameters: nil];
|
|
|
|
|
|
|
|
[_request setHeader: @"x-http-version"
|
|
|
|
value: version
|
|
|
|
parameters: nil];
|
2024-03-06 10:12:30 +00:00
|
|
|
|
2024-03-07 04:38:02 +00:00
|
|
|
NSDebugLog(@"%@: got request\n%@", self, _request);
|
|
|
|
|
2014-11-29 11:39:38 +00:00
|
|
|
_response = [GSMimeDocument new];
|
2024-03-07 04:38:02 +00:00
|
|
|
|
2014-11-29 11:55:16 +00:00
|
|
|
if (nil != _delegate && [_delegate respondsToSelector: @selector(processRequest:response:for:)])
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
ret = [_delegate processRequest: _request response: _response for: self];
|
|
|
|
}
|
2014-11-29 11:55:16 +00:00
|
|
|
if (!ret)
|
2014-11-29 11:39:38 +00:00
|
|
|
{
|
|
|
|
DESTROY(_response);
|
|
|
|
_response = [GSMimeDocument new];
|
|
|
|
[_response setHeader: @"HTTP" value: @"HTTP/1.1 204 No Content" parameters: nil];
|
|
|
|
[_response setHeader: @"Content-Length" value: @"0" parameters: nil];
|
|
|
|
ret = YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DESTROY(tmp1);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-03-06 10:12:30 +00:00
|
|
|
- (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]];
|
|
|
|
|
2024-03-07 04:38:02 +00:00
|
|
|
NSDebugLog(@"%@: about to send response\n%@", self, _response);
|
2024-03-06 10:12:30 +00:00
|
|
|
|
|
|
|
[_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);
|
|
|
|
}
|
|
|
|
|
2014-11-29 11:39:38 +00:00
|
|
|
@end /* SimpleWebServer (Private) */
|