libs-base/Tests/base/NSURLHandle/Helpers/StatusServer.m

375 lines
9 KiB
Mathematica
Raw Normal View History

#import <Foundation/Foundation.h>
#if GNUSTEP
#define PORT_LISTEN 54321
#define LOCATION200 @"http://localhost:54321/200"
@interface StatusServer : NSObject
{
NSInputStream *ip;
NSOutputStream *op;
NSDictionary *responses;
NSDictionary *headers;
NSDictionary *commonHeaders;
NSString *body;
NSMutableData *request;
}
- (int) runTest;
@end
@implementation StatusServer
- (id)init
{
if ((self = [super init]))
{
body = @"Hello\r\n";
unsigned bodyLength = [body length];
NSString *bl = [[NSNumber numberWithInt: bodyLength]
stringValue];
NSString *date = [[NSCalendarDate date] description];
// set up the dictionaries of responses and headers
commonHeaders = [NSDictionary dictionaryWithObjectsAndKeys:
@"close", @"Connection",
@"GNUstep test harness", @"Server",
@"text/plain", @"Content-Type",
date, @"Date",
nil];
[commonHeaders retain];
responses = [NSDictionary dictionaryWithObjectsAndKeys:
@"HTTP/1.1 200 OK", @"200",
@"HTTP/1.1 201 Created", @"201",
@"HTTP/1.1 204 No Content", @"204",
@"HTTP/1.1 301 Moved Permanently", @"301",
@"HTTP/1.1 302 Found", @"302",
@"HTTP/1.1 303 See Other", @"303",
@"HTTP/1.1 307 Temporary Redirect", @"307",
@"HTTP/1.1 400 Bad Request", @"400",
@"HTTP/1.1 401 Unauthorized", @"401",
@"HTTP/1.1 403 Forbidden", @"403",
@"HTTP/1.1 404 Not Found", @"404",
@"HTTP/1.1 416 Requested Range Not Satisfiable",
@"416",
@"HTTP/1.1 500 Internal Server Error", @"500",
@"HTTP/1.1 501 Not Implemented", @"501",
nil];
[responses retain];
NSDictionary *header200 = [NSDictionary dictionaryWithObjectsAndKeys:
bl, @"Content-Length",
nil];
//we can use this Location: header in header201 for the 30x, too
NSDictionary *header201 = [NSDictionary dictionaryWithObjectsAndKeys:
LOCATION200, @"Location",
nil];
NSDictionary *header401 = [NSDictionary dictionaryWithObjectsAndKeys:
@"Basic realm=\"GNUstep\"",
@"WWW-Authenticate",
nil];
headers = [NSDictionary dictionaryWithObjectsAndKeys:
header200, @"200",
header201, @"201",
header201, @"301",
header201, @"302",
header201, @"303",
header201, @"307",
header401, @"401",
nil];
[headers retain];
ip = nil;
op = nil;
request = nil;
}
return self;
}
- (void)dealloc
{
[headers release];
[commonHeaders release];
[responses release];
if (nil != ip)
{
[ip release];
}
if (nil != op)
{
[op release];
}
if (nil != request)
{
[request release];
}
[super dealloc];
}
- (int)runTest
{
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
NSRunLoop *rl = [NSRunLoop currentRunLoop];
NSHost *host = [NSHost hostWithName: @"localhost"];
NSStream *serverStream;
unsigned port = [[defs stringForKey: @"Port"] intValue];
if (0 == port) port = PORT_LISTEN;
serverStream = [GSServerStream serverStreamToAddr: [host address]
port: port];
if (nil == serverStream)
{
NSLog(@"Failed to create server stream");
return 1;
}
[serverStream setDelegate: self];
[serverStream scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
[serverStream open];
// run for one minute, then quit
[rl runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 60]];
return 0;
}
- (void) stream: (NSStream *)theStream handleEvent: (NSStreamEvent) streamEvent
{
NSRunLoop *rl = [NSRunLoop currentRunLoop];
switch (streamEvent)
{
case NSStreamEventHasBytesAvailable:
{
if (theStream != ip)
{
if (ip != nil)
{
[ip close];
[ip removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
ip = nil;
}
if (op != nil)
{
[op close];
[op removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
op = nil;
}
[(GSServerStream *)theStream acceptWithInputStream: &ip
outputStream: &op];
if (ip)
{
RETAIN(ip);
RETAIN(op);
[ip scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
[ip setDelegate: self];
[op setDelegate: self];
[ip open];
}
else
{
NSLog(@"Received nothing from accept");
}
}
else if (theStream == ip)
{
uint8_t buf[1024];
unsigned len;
BOOL done = NO;
if (nil != request)
{
[request release];
}
request = [[NSMutableData alloc] initWithCapacity: sizeof(buf)];
len = [ip read: buf maxLength: sizeof(buf)];
if (len > 0)
{
const char *bytes;
[request appendBytes: buf length: len];
len = [request length];
bytes = (const char*)[request bytes];
if (len > 3 && memcmp(bytes+len-4, "\r\n\r\n", 4) == 0)
{
done = YES;
}
}
else
{
done = YES; // EOF or error
}
if (done == YES)
{
[op open];
[op scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
[ip close];
[ip removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
ip = nil;
}
}
break;
}
case NSStreamEventHasSpaceAvailable:
{
NSString *wholeReq, *req, *retCode, *hdr;
NSMutableData *response;
NSArray *components;
NSDictionary *specificHeaders;
NSEnumerator *en;
int statusCode;
NSAssert(op == theStream, NSInternalInconsistencyException);
if (nil == request)
{
NSLog(@"Attempt to send response without a request");
return;
}
wholeReq = [[[NSString alloc] initWithData: request
encoding: NSASCIIStringEncoding]
autorelease];
response = [[[NSMutableData alloc] init] autorelease];
components = [wholeReq componentsSeparatedByString: @"\r\n"];
// the actual request is the first line
req = [components objectAtIndex: 0];
if ([req rangeOfString: @"GET"].location == NSNotFound
&& [req rangeOfString: @"HEAD"].location == NSNotFound)
{
retCode = @"501"; // HTTP 501: Not Implemented
}
else
{
retCode = [[req componentsSeparatedByString: @" "]
objectAtIndex: 1];
// trim the leading slash
retCode = [[retCode pathComponents] objectAtIndex: 1];
}
if ([responses objectForKey: retCode] == nil)
{
retCode = @"404"; // HTTP 404: Not Found
}
// build the response
[response appendBytes: [[responses objectForKey: retCode] cString]
length: [[responses objectForKey: retCode] length]];
[response appendBytes: "\r\n" length: 2];
en = [commonHeaders keyEnumerator];
while ((hdr = [en nextObject]) != nil)
{
[response appendBytes: [hdr cString] length: [hdr length]];
[response appendBytes: ": " length: 2];
hdr = [commonHeaders objectForKey: hdr];
[response appendBytes: [hdr cString] length: [hdr length]];
[response appendBytes: "\r\n" length: 2];
}
specificHeaders = [headers objectForKey: retCode];
if (specificHeaders != nil)
{
en = [specificHeaders keyEnumerator];
while ((hdr = [en nextObject]) != nil)
{
[response appendBytes: [hdr cString] length: [hdr length]];
[response appendBytes: ": " length: 2];
hdr = [specificHeaders objectForKey: hdr];
[response appendBytes: [hdr cString] length: [hdr length]];
[response appendBytes: "\r\n" length: 2];
}
}
[response appendBytes: "\r\n" length: 2];
//do we need to add the body part?
if([req rangeOfString: @"HEAD"].location == NSNotFound)
{
statusCode = [retCode intValue];
switch (statusCode)
{
case 200:
case 400:
case 401:
case 403:
case 404:
case 500:
case 501:
[response appendBytes: [body cString] length: [body length]];
break;
default:
break;
}
}
// send this response and close the stream
[op write: [response bytes] maxLength: [response length]];
[op close];
[op removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
op = nil;
break;
}
case NSStreamEventEndEncountered:
{
[theStream close];
[theStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
if (theStream == ip) ip = nil;
if (theStream == op) op = nil;
break;
}
case NSStreamEventErrorOccurred:
{
int code = [[theStream streamError] code];
NSLog(@"Error %d on stream %p", code, theStream);
[theStream close];
[theStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
if (theStream == ip) ip = nil;
if (theStream == op) op = nil;
break;
}
case NSStreamEventOpenCompleted:
break;
default:
NSLog (@"Event %d on stream %p unknown", streamEvent, theStream);
break;
}
}
@end
int main (int argc, char **argv)
{
int result;
NSAutoreleasePool *arp = [NSAutoreleasePool new];
result = [[[[StatusServer alloc] init] autorelease] runTest];
[arp release]; arp = nil;
return result;
}
#else
int main (int argc, char **argv)
{
NSAutoreleasePool *arp = [NSAutoreleasePool new];
NSLog(@"StatesServer not implemented on non-GNUstep systems");
[arp release]; arp = nil;
return 0;
}
#endif