This repository has been archived by the owner on Oct 9, 2020. It is now read-only.
forked from robbiehanson/CocoaHTTPServer
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1a7c4e9
commit f3da5ff
Showing
14 changed files
with
3,252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#import <Cocoa/Cocoa.h> | ||
|
||
@class HTTPServer; | ||
|
||
|
||
@interface AppDelegate : NSObject | ||
{ | ||
HTTPServer *httpServer; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#import "AppDelegate.h" | ||
#import "ThreadPoolServer.h" | ||
#import "ThreadPerConnectionServer.h" | ||
|
||
|
||
@implementation AppDelegate | ||
|
||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification | ||
{ | ||
httpServer = [[ThreadPoolServer alloc] init]; | ||
// httpServer = [[ThreadPerConnectionServer alloc] init]; | ||
|
||
[httpServer setType:@"_http._tcp."]; | ||
[httpServer setDocumentRoot:[NSURL fileURLWithPath:[@"~/Sites" stringByExpandingTildeInPath]]]; | ||
|
||
NSError *error = nil; | ||
BOOL success = [httpServer start:&error]; | ||
|
||
if(!success) | ||
{ | ||
NSLog(@"Error starting HTTP Server: %@", error); | ||
} | ||
} | ||
|
||
@end |
Binary file not shown.
2,447 changes: 2,447 additions & 0 deletions
2,447
Samples/MultiThreadedHTTPServer/English.lproj/MainMenu.nib/designable.nib
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file added
BIN
+17.9 KB
Samples/MultiThreadedHTTPServer/English.lproj/MainMenu.nib/keyedobjects.nib
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>English</string> | ||
<key>CFBundleExecutable</key> | ||
<string>${EXECUTABLE_NAME}</string> | ||
<key>CFBundleIconFile</key> | ||
<string></string> | ||
<key>CFBundleIdentifier</key> | ||
<string>com.yourcompany.MultiThreadedHTTPServer</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>${PRODUCT_NAME}</string> | ||
<key>CFBundlePackageType</key> | ||
<string>APPL</string> | ||
<key>CFBundleSignature</key> | ||
<string>????</string> | ||
<key>CFBundleVersion</key> | ||
<string>1.0</string> | ||
<key>NSMainNibFile</key> | ||
<string>MainMenu</string> | ||
<key>NSPrincipalClass</key> | ||
<string>NSApplication</string> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
Software License Agreement (BSD License) | ||
|
||
Copyright (c) 2006, Deusty Designs, LLC | ||
All rights reserved. | ||
|
||
Redistribution and use of this software in source and binary forms, | ||
with or without modification, are permitted provided that the following conditions are met: | ||
|
||
* Redistributions of source code must retain the above | ||
copyright notice, this list of conditions and the | ||
following disclaimer. | ||
|
||
* Neither the name of Desuty Designs nor the names of its | ||
contributors may be used to endorse or promote products | ||
derived from this software without specific prior | ||
written permission of Deusty Designs, LLC. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
437 changes: 437 additions & 0 deletions
437
Samples/MultiThreadedHTTPServer/MultiThreadedHTTPServer.xcodeproj/project.pbxproj
Large diffs are not rendered by default.
Oops, something went wrong.
7 changes: 7 additions & 0 deletions
7
Samples/MultiThreadedHTTPServer/MultiThreadedHTTPServer_Prefix.pch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// | ||
// Prefix header for all source files of the 'MultiThreadedHTTPServer' target in the 'MultiThreadedHTTPServer' project | ||
// | ||
|
||
#ifdef __OBJC__ | ||
#import <Cocoa/Cocoa.h> | ||
#endif |
25 changes: 25 additions & 0 deletions
25
Samples/MultiThreadedHTTPServer/ThreadPerConnectionServer.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// | ||
// The ThreadPerConnectionServer creates a new thread for every incoming connection. | ||
// After the thread is created, the connection is moved to the new thread. | ||
// | ||
// This is for DEMONSTRATION purposes only, and this technique will not scale well. | ||
// Please understand the cost of creating threads on your target platform. | ||
// | ||
|
||
#import "HTTPServer.h" | ||
#import "HTTPConnection.h" | ||
|
||
@interface ThreadPerConnectionServer : HTTPServer | ||
{ | ||
|
||
} | ||
|
||
@end | ||
|
||
@interface TPCConnection : HTTPConnection | ||
{ | ||
NSRunLoop *myRunLoop; | ||
BOOL continueRunLoop; | ||
} | ||
|
||
@end |
94 changes: 94 additions & 0 deletions
94
Samples/MultiThreadedHTTPServer/ThreadPerConnectionServer.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
#import "ThreadPerConnectionServer.h" | ||
#import "AsyncSocket.h" | ||
|
||
|
||
@interface HTTPConnection (InternalAPI) | ||
|
||
- (void)startReadingRequest; | ||
|
||
@end | ||
|
||
@implementation ThreadPerConnectionServer | ||
|
||
- (id)init | ||
{ | ||
if(self = [super init]) | ||
{ | ||
connectionClass = [TPCConnection self]; | ||
} | ||
return self; | ||
} | ||
|
||
@end | ||
|
||
@implementation TPCConnection | ||
|
||
- (id)initWithAsyncSocket:(AsyncSocket *)newSocket forServer:(HTTPServer *)myServer | ||
{ | ||
if(self = [super initWithAsyncSocket:newSocket forServer:myServer]) | ||
{ | ||
continueRunLoop = YES; | ||
[NSThread detachNewThreadSelector:@selector(setupRunLoop) toTarget:self withObject:nil]; | ||
|
||
// Note: The target of the thread is automatically retained, and released when the thread exits. | ||
} | ||
return self; | ||
} | ||
|
||
- (void)setupRunLoop | ||
{ | ||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | ||
|
||
@synchronized(self) | ||
{ | ||
myRunLoop = [NSRunLoop currentRunLoop]; | ||
} | ||
|
||
// Note: It is assumed the main listening socket is running on the main thread. | ||
// If this assumption is incorrect in your case, you'll need to call switchRunLoop on correct thread. | ||
[self performSelectorOnMainThread:@selector(switchRunLoop) withObject:nil waitUntilDone:YES]; | ||
|
||
[self startReadingRequest]; | ||
|
||
while (continueRunLoop) | ||
{ | ||
NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init]; | ||
|
||
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]]; | ||
|
||
NSLog(@"iteration"); | ||
[innerPool release]; | ||
} | ||
|
||
NSLog(@"%p: RunLoop closing down", self); | ||
|
||
[pool release]; | ||
} | ||
|
||
- (void)switchRunLoop | ||
{ | ||
@synchronized(self) | ||
{ | ||
// The moveToRunLoop method must be called on the socket's existing runloop/thread | ||
[asyncSocket moveToRunLoop:myRunLoop]; | ||
} | ||
|
||
NSLog(@"%p: Run loop up", self); | ||
} | ||
|
||
|
||
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port | ||
{ | ||
// Do nothing here - wait until the socket has been moved to the proper thread | ||
} | ||
|
||
/** | ||
* Called when the connection dies. | ||
**/ | ||
- (void)die | ||
{ | ||
continueRunLoop = NO; | ||
[super die]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// | ||
// The ThreadPoolServer uses a pool of threads to handle connections. | ||
// Each incoming connection is moved to a thread within the thread pool. | ||
// We attempt to evenly spread the connections between the threads. | ||
// To do this, we maintain the number of connections that are on each thread. | ||
// A new incoming connection will be placed on the thread with the least connections. | ||
// | ||
|
||
#import "HTTPServer.h" | ||
#import "HTTPConnection.h" | ||
|
||
// Define number of connection threads to run | ||
#define THREAD_POOL_SIZE 10 | ||
|
||
@interface ThreadPoolServer : HTTPServer | ||
{ | ||
NSMutableArray *runLoops; | ||
NSMutableArray *runLoopsLoad; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
#import "ThreadPoolServer.h" | ||
|
||
@interface HTTPServer (InternalAPI) | ||
|
||
- (void)connectionDidDie:(NSNotification *)notification; | ||
|
||
@end | ||
|
||
|
||
@implementation ThreadPoolServer | ||
|
||
- (id)init | ||
{ | ||
if(self = [super init]) | ||
{ | ||
// Initialize an array to reference all the threads | ||
runLoops = [[NSMutableArray alloc] initWithCapacity:THREAD_POOL_SIZE]; | ||
|
||
// Initialize an array to hold the number of connections being processed for each thread | ||
runLoopsLoad = [[NSMutableArray alloc] initWithCapacity:THREAD_POOL_SIZE]; | ||
|
||
// Start threads | ||
uint i; | ||
for(i = 0; i < THREAD_POOL_SIZE; i++) | ||
{ | ||
[NSThread detachNewThreadSelector:@selector(connectionThread:) | ||
toTarget:self | ||
withObject:[NSNumber numberWithUnsignedInt:i]]; | ||
} | ||
} | ||
return self; | ||
} | ||
|
||
|
||
- (void)connectionThread:(NSNumber *)threadNum | ||
{ | ||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | ||
|
||
@synchronized(runLoops) | ||
{ | ||
[runLoops addObject:[NSRunLoop currentRunLoop]]; | ||
[runLoopsLoad addObject:[NSNumber numberWithUnsignedInt:0]]; | ||
} | ||
|
||
NSLog(@"Starting thread %@", threadNum); | ||
|
||
// We can't run the run loop unless it has an associated input source or a timer. | ||
// So we'll just create a timer that will never fire - unless the server runs for 10,000 years. | ||
[NSTimer scheduledTimerWithTimeInterval:DBL_MAX target:self selector:@selector(ignore:) userInfo:nil repeats:NO]; | ||
|
||
// Start the run loop | ||
[[NSRunLoop currentRunLoop] run]; | ||
|
||
[pool release]; | ||
} | ||
|
||
/** | ||
* Called when a new socket is spawned to handle a connection. This method should return the runloop of the | ||
* thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used. | ||
**/ | ||
- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket | ||
{ | ||
// Figure out what thread/runloop to run the new connection on. | ||
// We choose the thread/runloop with the lowest number of connections. | ||
|
||
uint m = 0; | ||
NSRunLoop *mLoop = nil; | ||
uint mLoad = 0; | ||
|
||
@synchronized(runLoops) | ||
{ | ||
mLoop = [runLoops objectAtIndex:0]; | ||
mLoad = [[runLoopsLoad objectAtIndex:0] unsignedIntValue]; | ||
|
||
uint i; | ||
for(i = 1; i < THREAD_POOL_SIZE; i++) | ||
{ | ||
uint iLoad = [[runLoopsLoad objectAtIndex:i] unsignedIntValue]; | ||
|
||
if(iLoad < mLoad) | ||
{ | ||
m = i; | ||
mLoop = [runLoops objectAtIndex:i]; | ||
mLoad = iLoad; | ||
} | ||
} | ||
|
||
[runLoopsLoad replaceObjectAtIndex:m withObject:[NSNumber numberWithUnsignedInt:(mLoad + 1)]]; | ||
} | ||
|
||
NSLog(@"Choosing run loop %u with load %u", m, mLoad); | ||
|
||
// And finally, return the proper run loop | ||
return mLoop; | ||
} | ||
|
||
/** | ||
* This method is automatically called when a HTTPConnection dies. | ||
* We need to update the number of connections per thread. | ||
**/ | ||
- (void)connectionDidDie:(NSNotification *)notification | ||
{ | ||
// Note: This method is called on the thread/runloop that posted the notification | ||
|
||
@synchronized(runLoops) | ||
{ | ||
unsigned int runLoopIndex = [runLoops indexOfObject:[NSRunLoop currentRunLoop]]; | ||
|
||
if(runLoopIndex < [runLoops count]) | ||
{ | ||
unsigned int runLoopLoad = [[runLoopsLoad objectAtIndex:runLoopIndex] unsignedIntValue]; | ||
|
||
NSNumber *newLoad = [NSNumber numberWithUnsignedInt:(runLoopLoad - 1)]; | ||
|
||
[runLoopsLoad replaceObjectAtIndex:runLoopIndex withObject:newLoad]; | ||
|
||
NSLog(@"Updating run loop %u with load %@", runLoopIndex, newLoad); | ||
} | ||
} | ||
|
||
// Don't forget to call super, or the connection won't get proper deallocated! | ||
[super connectionDidDie:notification]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// | ||
// main.m | ||
// MultiThreadedHTTPServer | ||
// | ||
// Created by Robbie Hanson on 8/15/08. | ||
// Copyright __MyCompanyName__ 2008. All rights reserved. | ||
// | ||
|
||
#import <Cocoa/Cocoa.h> | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
return NSApplicationMain(argc, (const char **) argv); | ||
} |