commit | dfd37078ae77a626f4fde2818de6a607f2aefaec | [log] [tgz] |
---|---|---|
author | Pierre-Olivier Latour <pol@mac.com> | Thu Apr 10 21:27:20 2014 |
committer | Pierre-Olivier Latour <pol@mac.com> | Thu Apr 10 21:27:20 2014 |
tree | e0f25a621c2de7336153f3b47d8a4925ff429d39 | |
parent | 582c6da74fcf7964eaab82dbfdebb086c9e1c370 [diff] |
Fix
GCDWebServer is a lightweight GCD based HTTP 1.1 server designed to be embedded in Mac & iOS apps. It was written from scratch with the following goals in mind:
Extra built-in features:
Included extensions:
What's not available out of the box but can be implemented on top of the API:
What's not supported (but not really required from an embedded HTTP server):
Requirements:
Download or checkout the source for GCDWebServer then add the entire “GCDWebServer” subfolder to your Xcode project. If you intend to use one of the extensions like GCDWebDAVServer or GCDWebUploader, add these subfolders as well.
Alternatively, you can install GCDWebServer using CocoaPods by simply adding this line to your Xcode project's Podfile:
pod "GCDWebServer", "~> 2.0"
This code snippet shows how to implement a custom HTTP server that runs on port 8080 and returns a “Hello World” HTML page to any request — since GCDWebServer uses GCD blocks to handle requests, no subclassing or delegates are needed:
#import "GCDWebServer.h" int main(int argc, const char* argv[]) { @autoreleasepool { // Create server GCDWebServer* webServer = [[GCDWebServer alloc] init]; // Add a handler to respond to requests on any URL [webServer addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { return [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"]; }]; // Use convenience method that runs server on port 8080 until SIGINT received [webServer runWithPort:8080]; // Destroy server [webServer release]; } return 0; }
GCDWebUploader is a subclass of GCDWebServer that provides a ready-to-use HTML 5 file uploader & downloader. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using a clean user interface in their web browser.
Simply instantiate and run a GCDWebUploader instance then visit http://{YOUR-IOS-DEVICE-IP-ADDRESS}/ from your web browser:
#import "GCDWebUploader.h" - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; GCDWebUploader* webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath]; [webUploader start]; NSLog(@"Visit %@ in your web browser", webUploader.serverURL); return YES; }
GCDWebDAVServer is a subclass of GCDWebServer that provides a class 1 compliant WebDAV server. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using any WebDAV client like Transmit (Mac), ForkLift (Mac) or CyberDuck (Mac / Windows).
GCDWebDAVServer should also work with the OS X Finder as it is partially class 2 compliant (but only when the client is the OS X WebDAV implementation).
Simply instantiate and run a GCDWebDAVServer instance then connect to http://{YOUR-IOS-DEVICE-IP-ADDRESS}/ using a WebDAV client:
#import "GCDWebDAVServer.h" - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; GCDWebDAVServer* davServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:documentsPath]; [davServer start]; NSLog(@"Visit %@ in your WebDAV client", davServer.serverURL); return YES; }
GCDWebServer includes a built-in handler that can recursively serve a directory (it also lets you control how the “Cache-Control” header should be set):
#import "GCDWebServer.h" int main(int argc, const char* argv[]) { @autoreleasepool { GCDWebServer* webServer = [[GCDWebServer alloc] init]; [webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:3600 allowRangeRequests:YES]; [webServer runWithPort:8080]; [webServer release]; } return 0; }
You start by creating an instance of the ‘GCDWebServer’ class. Note that you can have multiple web servers running in the same app as long as they listen on different ports.
Then you add one or more “handlers” to the server: each handler gets a chance to handle an incoming web request and provide a response. Handlers are called in a LIFO queue, so the latest added handler overrides any previously added ones.
Finally you start the server on a given port.
GCDWebServer is made of only 4 core classes:
GCDWebServer relies on “handlers” to process incoming web requests and generating responses. Handlers are implemented with GCD blocks which makes it very easy to provide your owns. However, they are executed on arbitrary threads within GCD so special attention must be paid to thread-safety and re-entrancy.
Handlers require 2 GCD blocks:
Note that most methods on ‘GCDWebServer’ to add handlers only require the ‘GCDWebServerProcessBlock’ as they already provide a built-in ‘GCDWebServerMatchBlock’ e.g. to match a URL path with a Regex.
Here's an example handler that redirects “/” to “/index.html” using the convenience method on ‘GCDWebServerResponse’ (it sets the HTTP status code and ‘Location’ header automatically):
[self addHandlerForMethod:@"GET" path:@"/" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { return [GCDWebServerResponse responseWithRedirect:[NSURL URLWithString:@"index.html" relativeToURL:request.URL] permanent:NO]; }];
To implement an HTTP form, you need a pair of handlers:
[webServer addHandlerForMethod:@"GET" path:@"/" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { NSString* html = @" \ <html><body> \ <form name=\"input\" action=\"/\" method=\"post\" enctype=\"application/x-www-form-urlencoded\"> \ Value: <input type=\"text\" name=\"value\"> \ <input type=\"submit\" value=\"Submit\"> \ </form> \ </body></html> \ "; return [GCDWebServerDataResponse responseWithHTML:html]; }]; [webServer addHandlerForMethod:@"POST" path:@"/" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"]; NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p></body></html>", value]; return [GCDWebServerDataResponse responseWithHTML:html]; }];
GCDWebServer provides an extension to the ‘GCDWebServerDataResponse’ class that can return HTML content generated from a template and a set of variables (using the format ‘%variable%’). It is a very basic template system and is really intended as a starting point to building more advanced template systems by subclassing ‘GCDWebServerResponse’.
Assuming you have a website directory in your app containing HTML template files along with the corresponding CSS, scripts and images, it's pretty easy to turn it into a dynamic website:
// Get the path to the website directory NSString* websitePath = [[NSBundle mainBundle] pathForResource:@"Website" ofType:nil]; // Add a default handler to serve static files (i.e. anything other than HTML files) [self addGETHandlerForBasePath:@"/" directoryPath:websitePath indexFilename:nil cacheAge:3600 allowRangeRequests:YES]; // Add an override handler for all requests to "*.html" URLs to do the special HTML templatization [self addHandlerForMethod:@"GET" pathRegex:@"/.*\.html" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { NSDictionary* variables = [NSDictionary dictionaryWithObjectsAndKeys:@"value", @"variable", nil]; return [GCDWebServerDataResponse responseWithHTMLTemplate:[websitePath stringByAppendingPathComponent:request.path] variables:variables]; }]; // Add an override handler to redirect "/" URL to "/index.html" [self addHandlerForMethod:@"GET" path:@"/" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { return [GCDWebServerResponse responseWithRedirect:[NSURL URLWithString:@"index.html" relativeToURL:request.URL] permanent:NO]; ];
GCDWebServer was originally written for the ComicFlow comic reader app for iPad. It allow users to connect to their iPad with their web browser over WiFi and then upload, download and organize comic files inside the app.
ComicFlow is entirely open-source and you can see how it uses GCDWebUploader in the WebServer.m file.