aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2010-06-03 18:36:24 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2010-06-03 18:36:24 +0000
commit8ceff783b1fd62b67c7a6301228af9f7266e183b (patch)
tree9af9ead06d778243e042f4a04dff8d8ce3f0757f
parent2ac0ec70f08131a1a5b3e402fb013efb65279813 (diff)
[Author: ebelin]
GTMHTTPServer - support port reuse, plain text responses R=dmaclach,thomasvl APPROVED=thomasvl DELTA=70 (67 added, 0 deleted, 3 changed)
-rw-r--r--Foundation/GTMHTTPServer.h11
-rw-r--r--Foundation/GTMHTTPServer.m18
-rw-r--r--Foundation/GTMHTTPServerTest.m44
3 files changed, 70 insertions, 3 deletions
diff --git a/Foundation/GTMHTTPServer.h b/Foundation/GTMHTTPServer.h
index 0caa149..3920968 100644
--- a/Foundation/GTMHTTPServer.h
+++ b/Foundation/GTMHTTPServer.h
@@ -64,13 +64,14 @@ enum {
@private
__weak id delegate_;
uint16_t port_;
+ BOOL reusePort_;
BOOL localhostOnly_;
NSFileHandle *listenHandle_;
NSMutableArray *connections_;
}
// The delegate must support the httpServer:handleRequest: method in
-// NSObject(GTMHTTPServerDeletateMethods) below.
+// NSObject(GTMHTTPServerDelegateMethods) below.
- (id)initWithDelegate:(id)delegate;
- (id)delegate;
@@ -79,6 +80,11 @@ enum {
- (uint16_t)port;
- (void)setPort:(uint16_t)port;
+// Controls listening socket behavior: SO_REUSEADDR vs SO_REUSEPORT.
+// The default is NO (SO_REUSEADDR)
+- (BOOL)reusePort;
+- (void)setReusePort:(BOOL)reusePort;
+
// Receive connections on the localHost loopback address only or on all
// interfaces for this machine. The default is to only listen on localhost.
- (BOOL)localhostOnly;
@@ -98,7 +104,7 @@ enum {
@end
-@interface NSObject (GTMHTTPServerDeletateMethods)
+@interface NSObject (GTMHTTPServerDelegateMethods)
- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server
handleRequest:(GTMHTTPRequestMessage *)request;
@end
@@ -126,6 +132,7 @@ enum {
@private
CFHTTPMessageRef message_;
}
++ (id)responseWithString:(NSString *)plainText;
+ (id)responseWithHTMLString:(NSString *)htmlString;
+ (id)responseWithBody:(NSData *)body
contentType:(NSString *)contentType
diff --git a/Foundation/GTMHTTPServer.m b/Foundation/GTMHTTPServer.m
index a514842..57e6077 100644
--- a/Foundation/GTMHTTPServer.m
+++ b/Foundation/GTMHTTPServer.m
@@ -114,6 +114,14 @@ static NSString *kResponse = @"Response";
port_ = port;
}
+- (BOOL)reusePort {
+ return reusePort_;
+}
+
+- (void)setReusePort:(BOOL)yesno {
+ reusePort_ = yesno;
+}
+
- (BOOL)localhostOnly {
return localhostOnly_;
}
@@ -139,7 +147,8 @@ static NSString *kResponse = @"Response";
// enable address reuse quicker after we are done w/ our socket
int yes = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ int sock_opt = reusePort_ ? SO_REUSEPORT : SO_REUSEADDR;
+ if (setsockopt(fd, SOL_SOCKET, sock_opt,
(void *)&yes, (socklen_t)sizeof(yes)) != 0) {
_GTMDevLog(@"failed to mark the socket as reusable"); // COV_NF_LINE
}
@@ -526,6 +535,13 @@ startFailed:
[super dealloc];
}
++ (id)responseWithString:(NSString *)plainText {
+ NSData *body = [plainText dataUsingEncoding:NSUTF8StringEncoding];
+ return [self responseWithBody:body
+ contentType:@"text/plain; charset=UTF-8"
+ statusCode:200];
+}
+
+ (id)responseWithHTMLString:(NSString *)htmlString {
return [self responseWithBody:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
contentType:@"text/html; charset=UTF-8"
diff --git a/Foundation/GTMHTTPServerTest.m b/Foundation/GTMHTTPServerTest.m
index 11ce8f2..0cafaa2 100644
--- a/Foundation/GTMHTTPServerTest.m
+++ b/Foundation/GTMHTTPServerTest.m
@@ -150,6 +150,28 @@ const NSTimeInterval kSendChunkInterval = 0.05;
[server2 stop];
}
+- (void)testRestart {
+ TestServerDelegate *delegate = [TestServerDelegate testServerDelegate];
+ STAssertNotNil(delegate, nil);
+ GTMHTTPServer *server =
+ [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease];
+ STAssertNotNil(server, nil);
+ [server setLocalhostOnly:YES];
+ [server setReusePort:YES];
+ NSError *error = nil;
+ STAssertTrue([server start:&error], @"failed to start (error=%@)", error);
+ STAssertNil(error, @"error: %@", error);
+ uint16_t prevPort = [server port];
+ STAssertGreaterThanOrEqual(prevPort, (uint16_t)1024,
+ @"how'd we get a reserved port?");
+ // restart and make sure it works
+ [server stop];
+ error = nil;
+ [server setPort:prevPort];
+ STAssertTrue([server start:&error], @"failed to start (error=%@)", error);
+ STAssertNil(error, @"error: %@", error);
+}
+
- (void)testRequests {
TestServerDelegate *delegate = [TestServerDelegate testServerDelegate];
STAssertNotNil(delegate, nil);
@@ -328,6 +350,28 @@ const NSTimeInterval kSendChunkInterval = 0.05;
STAssertNotEquals([responseString rangeOfString:@"Custom-Header2: "].location,
(NSUInteger)NSNotFound, @"String: %@", responseString);
+ // test plain text response
+
+ expectedResponse = [GTMHTTPResponseMessage responseWithString:@"BAR"];
+ STAssertNotNil(expectedResponse, nil);
+ STAssertGreaterThan([[expectedResponse description] length],
+ (NSUInteger)3, nil);
+ [delegate pushResponse:expectedResponse];
+ responseData = [self fetchFromPort:[server port]
+ payload:@"GET /bar HTTP/1.0\r\n\r\n"
+ chunkSize:kSendChunkSize];
+ STAssertNotNil(responseData, nil);
+ responseString =
+ [[[NSString alloc] initWithData:responseData
+ encoding:NSUTF8StringEncoding] autorelease];
+ STAssertNotNil(responseString, nil);
+ STAssertTrue([responseString hasPrefix:@"HTTP/1.0 200 "], nil);
+ STAssertTrue([responseString hasSuffix:@"BAR"], @"should end w/ our data");
+ STAssertNotEquals([responseString rangeOfString:@"Content-Length: 3"].location,
+ (NSUInteger)NSNotFound, nil);
+ STAssertNotEquals([responseString rangeOfString:@"Content-Type: text/plain"].location,
+ (NSUInteger)NSNotFound, nil);
+
[server stop];
}