meteor/GCDWebServer

Name: GCDWebServer

Owner: Meteor

Description: Lightweight GCD based HTTP server for OS X & iOS (includes web based uploader & WebDAV server)

Forked from: swisspol/GCDWebServer

Created: 2018-01-17 19:09:30.0

Updated: 2018-01-17 19:09:32.0

Pushed: 2017-12-20 14:41:11.0

Homepage:

Size: 12175

Language: Objective-C

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Overview

Build Status Version Platform License

GCDWebServer is a modern and lightweight GCD based HTTP 1.1 server designed to be embedded in OS X & iOS apps. It was written from scratch with the following goals in mind:

Extra built-in features:

Included extensions:

What's not supported (but not really required from an embedded HTTP server):

Requirements:

Getting Started

Download or check out the latest release of 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.

If you add the files directly then (1) link to libz (via Target > Build Phases > Link Binary With Libraries) and (2) add $(SDKROOT)/usr/include/libxml2 to your header search paths (via Target > Build Settings > HEADER_SEARCH_PATHS).

Alternatively, you can install GCDWebServer using CocoaPods by simply adding this line to your Podfile:

"GCDWebServer", "~> 3.0"

If you want to use GCDWebUploader, use this line instead:

"GCDWebServer/WebUploader", "~> 3.0"

Or this line for GCDWebDAVServer:

"GCDWebServer/WebDAV", "~> 3.0"

And finally run $ pod install.

You can also use Carthage by adding this line to your Cartfile (3.2.5 is the first release with Carthage support):

ub "swisspol/GCDWebServer" ~> 3.2.5

Then run $ carthage update and add the generated frameworks to your Xcode projects (see Carthage instructions).

Help & Support

For help with using GCDWebServer, it's best to ask your question on Stack Overflow with the gcdwebserver tag. For bug reports and enhancement requests you can use issues in this project.

Be sure to read this entire README first though!

Hello World

These code snippets show 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, which results in very clean code.

IMPORTANT: If not using CocoaPods, be sure to add the libz shared system library to the Xcode target for your app.

OS X version (command line tool):

ort "GCDWebServer.h"
ort "GCDWebServerDataResponse.h"

main(int argc, const char* argv[]) {
utoreleasepool {

// Create server
GCDWebServer* webServer = [[GCDWebServer alloc] init];

// Add a handler to respond to GET 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 (Ctrl-C in Terminal) or SIGTERM is received
[webServer runWithPort:8080 bonjourName:nil];
NSLog(@"Visit %@ in your web browser", webServer.serverURL);


turn 0;

iOS version:

ort "GCDWebServer.h"
ort "GCDWebServerDataResponse.h"

erface AppDelegate : NSObject <UIApplicationDelegate> {
DWebServer* _webServer;



lementation AppDelegate

OOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {

 Create server
ebServer = [[GCDWebServer alloc] init];

 Add a handler to respond to GET 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>"];

;

 Start server on port 8080
webServer startWithPort:8080 bonjourName:nil];
Log(@"Visit %@ in your web browser", _webServer.serverURL);

turn YES;



OS X Swift version (command line tool):

webServer.swift

rt Foundation
rt GCDWebServer

 initWebServer() {

let webServer = GCDWebServer()

webServer.addDefaultHandlerForMethod("GET", requestClass: GCDWebServerRequest.self, processBlock: {request in
return GCDWebServerDataResponse(HTML:"<html><body><p>Hello World</p></body></html>")

})

webServer.runWithPort(8080, bonjourName: "GCD Web Server")

print("Visit \(webServer.serverURL) in your web browser")

WebServer-Bridging-Header.h

ort <GCDWebServer/GCDWebServer.h>
ort <GCDWebServer/GCDWebServerDataResponse.h>

Web Based Uploads in iOS Apps

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 `GCDWebUploaderinstance then visit ``http://{YOUR-IOS-DEVICE-IP-ADDRESS}/``` from your web browser:

ort "GCDWebUploader.h"

erface AppDelegate : NSObject <UIApplicationDelegate> {
DWebUploader* _webUploader;



lementation AppDelegate

OOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
String* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
ebUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
webUploader start];
Log(@"Visit %@ in your web browser", _webUploader.serverURL);
turn YES;



WebDAV Server in iOS Apps

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 `GCDWebDAVServerinstance then connect to ``http://{YOUR-IOS-DEVICE-IP-ADDRESS}/``` using a WebDAV client:

ort "GCDWebDAVServer.h"

erface AppDelegate : NSObject <UIApplicationDelegate> {
DWebDAVServer* _davServer;



lementation AppDelegate

OOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
String* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
avServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:documentsPath];
davServer start];
Log(@"Visit %@ in your WebDAV client", _davServer.serverURL);
turn YES;



Serving a Static Website

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):

OS X version (command line tool):

ort "GCDWebServer.h"

main(int argc, const char* argv[]) {
utoreleasepool {

GCDWebServer* webServer = [[GCDWebServer alloc] init];
[webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:3600 allowRangeRequests:YES];
[webServer runWithPort:8080];


turn 0;

Using GCDWebServer

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.

Understanding GCDWebServer's Architecture

GCDWebServer's architecture consists of only 4 core classes:

Implementing Handlers

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 own. 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 `GCDWebServerto add handlers only require the ``GCDWebServerProcessBlock` orGCDWebServerAsyncProcessBlock`` as they already provide a built-in `GCDWebServerMatchBlock` e.g. to match a URL path with a Regex.

Asynchronous HTTP Responses

New in GCDWebServer 3.0 is the ability to process HTTP requests asynchronously i.e. add handlers to the server which generate their `GCDWebServerResponseasynchronously. This is achieved by adding handlers that use a ``GCDWebServerAsyncProcessBlock` instead of aGCDWebServerProcessBlock``. Here's an example:

(Synchronous version) The handler blocks while generating the HTTP response:

Server addDefaultHandlerForMethod:@"GET"
                     requestClass:[GCDWebServerRequest class]
                     processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

DWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
turn response;


(Asynchronous version) The handler returns immediately and calls back GCDWebServer later with the generated HTTP response:

Server addDefaultHandlerForMethod:@"GET"
                     requestClass:[GCDWebServerRequest class]
                asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {

 Do some async operation like network access or file I/O (simulated here using dispatch_after())
spatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
completionBlock(response);
;


(Advanced asynchronous version) The handler returns immediately a streamed HTTP response which itself generates its contents asynchronously:

Server addDefaultHandlerForMethod:@"GET"
                     requestClass:[GCDWebServerRequest class]
                     processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

MutableArray* contents = [NSMutableArray arrayWithObjects:@"<html><body><p>\n", @"Hello World!\n", @"</p></body></html>\n", nil];  // Fake data source we are reading from
DWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/html" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {

// Simulate a delay reading from the fake data source
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  NSString* string = contents.firstObject;
  if (string) {
    [contents removeObjectAtIndex:0];
    completionBlock([string dataUsingEncoding:NSUTF8StringEncoding], nil);  // Generate the 2nd part of the stream data
  } else {
    completionBlock([NSData data], nil);  // Must pass an empty NSData to signal the end of the stream
  }
});

;
turn response;


Note that you can even combine both the asynchronous and advanced asynchronous versions to return asynchronously an asynchronous HTTP response!

GCDWebServer & Background Mode for iOS Apps

When doing networking operations in iOS apps, you must handle carefully what happens when iOS puts the app in the background. Typically you must stop any network servers while the app is in the background and restart them when the app comes back to the foreground. This can become quite complex considering servers might have ongoing connections when they need to be stopped.

Fortunately, GCDWebServer does all of this automatically for you:

HTTP connections are often initiated in batches (or bursts), for instance when loading a web page with multiple resources. This makes it difficult to accurately detect when the very last HTTP connection has been closed: it's possible 2 consecutive HTTP connections part of the same batch would be separated by a small delay instead of overlapping. It would be bad for the client if GCDWebServer suspended itself right in between. The `GCDWebServerOption_ConnectedStateCoalescingInterval` option solves this problem elegantly by forcing GCDWebServer to wait some extra delay before performing any action after the last HTTP connection has been closed, just in case a new HTTP connection is initiated within this delay.

Logging in GCDWebServer

Both for debugging and informational purpose, GCDWebServer logs messages extensively whenever something happens. Furthermore, when building GCDWebServer in “Debug” mode versus “Release” mode, it logs even more information but also performs a number of internal consistency checks. To enable this behavior, define the preprocessor constant `DEBUG=1when compiling GCDWebServer. In Xcode target settings, this can be done by adding ``DEBUG=1` to the build settingGCC_PREPROCESSOR_DEFINITIONS`` when building in “Debug” configuration. Finally, you can also control the logging verbosity at run time by calling `+[GCDWebServer setLogLevel:]`.

By default, all messages logged by GCDWebServer are sent to its built-in logging facility, which simply outputs to `stderr` (assuming a terminal type device is connected). In order to better integrate with the rest of your app or because of the amount of information logged, you might want to use another logging facility.

GCDWebServer has automatic support for XLFacility (by the same author as GCDWebServer and also open-source): if it is in the same Xcode project, GCDWebServer should use it automatically instead of the built-in logging facility (see GCDWebServerPrivate.h for the implementation details).

It's also possible to use a custom logging facility - see GCDWebServer.h for more information.

Advanced Example 1: Implementing HTTP Redirects

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):

f addHandlerForMethod:@"GET"
                 path:@"/"
         requestClass:[GCDWebServerRequest class]
         processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

turn [GCDWebServerResponse responseWithRedirect:[NSURL URLWithString:@"index.html" relativeToURL:request.URL]
                                      permanent:NO];


Advanced Example 2: Implementing Forms

To implement an HTTP form, you need a pair of handlers:

Server addHandlerForMethod:@"GET"
                      path:@"/"
              requestClass:[GCDWebServerRequest class]
              processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

String* 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> \

turn [GCDWebServerDataResponse responseWithHTML:html];



Server addHandlerForMethod:@"POST"
                      path:@"/"
              requestClass:[GCDWebServerURLEncodedFormRequest class]
              processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

String* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"];
String* html = [NSString stringWithFormat:@"<html><body><p>%@</p></body></html>", value];
turn [GCDWebServerDataResponse responseWithHTML:html];


Advanced Example 3: Serving a Dynamic Website

GCDWebServer provides an extension to the `GCDWebServerDataResponseclass 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 subclassingGCDWebServerResponse``.

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:

et the path to the website directory
ring* websitePath = [[NSBundle mainBundle] pathForResource:@"Website" ofType:nil];

dd a default handler to serve static files (i.e. anything other than HTML files)
f addGETHandlerForBasePath:@"/" directoryPath:websitePath indexFilename:nil cacheAge:3600 allowRangeRequests:YES];

dd an override handler for all requests to "*.html" URLs to do the special HTML templatization
f 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];



dd an override handler to redirect "/" URL to "/index.html"
f addHandlerForMethod:@"GET"
                 path:@"/"
         requestClass:[GCDWebServerRequest class]
         processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

return [GCDWebServerResponse responseWithRedirect:[NSURL URLWithString:@"index.html" relativeToURL:request.URL]
                                        permanent:NO];


Final Example: File Downloads and Uploads From iOS App

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 GCDWebServer in the WebServer.h and WebServer.m files.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.