GCDWebServer is a modern and lightweight GCD based HTTP 1.1 server designed to be embedded in iOS, macOS & tvOS apps.

Overview

Overview

Build Status Version Platform License

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

  • Elegant and easy to use architecture with only 4 core classes: server, connection, request and response (see "Understanding GCDWebServer's Architecture" below)
  • Well designed API with fully documented headers for easy integration and customization
  • Entirely built with an event-driven design using Grand Central Dispatch for best performance and concurrency
  • No dependencies on third-party source code
  • Available under a friendly New BSD License

Extra built-in features:

  • Allow implementation of fully asynchronous handlers of incoming HTTP requests
  • Minimize memory usage with disk streaming of large HTTP request or response bodies
  • Parser for web forms submitted using "application/x-www-form-urlencoded" or "multipart/form-data" encodings (including file uploads)
  • JSON parsing and serialization for request and response HTTP bodies
  • Chunked transfer encoding for request and response HTTP bodies
  • HTTP compression with gzip for request and response HTTP bodies
  • HTTP range support for requests of local files
  • Basic and Digest Access authentications for password protection
  • Automatically handle transitions between foreground, background and suspended modes in iOS apps
  • Full support for both IPv4 and IPv6
  • NAT port mapping (IPv4 only)

Included extensions:

  • GCDWebUploader: subclass of GCDWebServer that implements an interface for uploading and downloading files using a web browser
  • GCDWebDAVServer: subclass of GCDWebServer that implements a class 1 WebDAV server (with partial class 2 support for macOS Finder)

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

  • Keep-alive connections
  • HTTPS

Requirements:

  • macOS 10.7 or later (x86_64)
  • iOS 8.0 or later (armv7, armv7s or arm64)
  • tvOS 9.0 or later (arm64)
  • ARC memory management only (if you need MRC support use GCDWebServer 3.1 or earlier)

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. Finally link to libz (via Target > Build Phases > Link Binary With Libraries) and 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:

3.0" ">
pod "GCDWebServer", "~> 3.0"

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

3.0" ">
pod "GCDWebServer/WebUploader", "~> 3.0"

Or this line for GCDWebDAVServer:

3.0" ">
pod "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):

3.2.5 ">
github "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.

macOS version (command line tool):

Hello World

"]; }]; // 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); } return 0; } ">
#import "GCDWebServer.h"
#import "GCDWebServerDataResponse.h"

int main(int argc, const char* argv[]) {
  @autoreleasepool {
    
    // 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:@"

Hello World

"
]; }]; // 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); } return 0; }

iOS version:

{ GCDWebServer* _webServer; } @end @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { // Create server _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:@"

Hello World

"]; }]; // Start server on port 8080 [_webServer startWithPort:8080 bonjourName:nil]; NSLog(@"Visit %@ in your web browser", _webServer.serverURL); return YES; } @end ">
#import "GCDWebServer.h"
#import "GCDWebServerDataResponse.h"

@interface AppDelegate : NSObject 
    {
  GCDWebServer* _webServer;
}

   @end


   @implementation 
   AppDelegate

- (
   BOOL)
   application
   :(UIApplication*)
   application 
   didFinishLaunchingWithOptions
   :(
   NSDictionary*)
   launchOptions {
  
  
   // Create server
  _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:
   @"

Hello World

"
]; }]; // Start server on port 8080 [_webServer startWithPort: 8080 bonjourName: nil]; NSLog( @"Visit %@ in your web browser", _webServer. serverURL); return YES; } @end

macOS Swift version (command line tool):

webServer.swift

Hello World

") }) webServer.start(withPort: 8080, bonjourName: "GCD Web Server") print("Visit \(webServer.serverURL) in your web browser") } ">
import Foundation
import GCDWebServer

func initWebServer() {

    let webServer = GCDWebServer()

    webServer.addDefaultHandler(forMethod: "GET", request: GCDWebServerRequest.self, processBlock: {request in
            return GCDWebServerDataResponse(html:"

Hello World

"
) }) webServer.start(withPort: 8080, bonjourName: "GCD Web Server") print("Visit \(webServer.serverURL) in your web browser") }

WebServer-Bridging-Header.h

#import <GCDWebServer/GCDWebServer.h>
#import <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 GCDWebUploader instance then visit http://{YOUR-IOS-DEVICE-IP-ADDRESS}/ from your web browser:

{ GCDWebUploader* _webUploader; } @end @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; _webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath]; [_webUploader start]; NSLog(@"Visit %@ in your web browser", _webUploader.serverURL); return YES; } @end ">
#import "GCDWebUploader.h"

@interface AppDelegate : NSObject 
    {
  GCDWebUploader* _webUploader;
}

   @end


   @implementation 
   AppDelegate

- (
   BOOL)
   application
   :(UIApplication*)
   application 
   didFinishLaunchingWithOptions
   :(
   NSDictionary*)
   launchOptions {
  
   NSString* documentsPath = [
   NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, 
   YES) 
   firstObject];
  _webUploader = [[GCDWebUploader 
   alloc] 
   initWithUploadDirectory:documentsPath];
  [_webUploader 
   start];
  
   NSLog(
   @"Visit %@ in your web browser", _webUploader.
   serverURL);
  
   return 
   YES;
}


   @end
  

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 macOS Finder as it is partially class 2 compliant (but only when the client is the macOS WebDAV implementation).

Simply instantiate and run a GCDWebDAVServer instance then connect to http://{YOUR-IOS-DEVICE-IP-ADDRESS}/ using a WebDAV client:

{ GCDWebDAVServer* _davServer; } @end @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; _davServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:documentsPath]; [_davServer start]; NSLog(@"Visit %@ in your WebDAV client", _davServer.serverURL); return YES; } @end ">
#import "GCDWebDAVServer.h"

@interface AppDelegate : NSObject 
    {
  GCDWebDAVServer* _davServer;
}

   @end


   @implementation 
   AppDelegate

- (
   BOOL)
   application
   :(UIApplication*)
   application 
   didFinishLaunchingWithOptions
   :(
   NSDictionary*)
   launchOptions {
  
   NSString* documentsPath = [
   NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, 
   YES) 
   firstObject];
  _davServer = [[GCDWebDAVServer 
   alloc] 
   initWithUploadDirectory:documentsPath];
  [_davServer 
   start];
  
   NSLog(
   @"Visit %@ in your WebDAV client", _davServer.
   serverURL);
  
   return 
   YES;
}


   @end
  

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

macOS version (command line tool):

#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];
    
  }
  return 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:

  • GCDWebServer manages the socket that listens for new HTTP connections and the list of handlers used by the server.
  • GCDWebServerConnection is instantiated by GCDWebServer to handle each new HTTP connection. Each instance stays alive until the connection is closed. You cannot use this class directly, but it is exposed so you can subclass it to override some hooks.
  • GCDWebServerRequest is created by the GCDWebServerConnection instance after HTTP headers have been received. It wraps the request and handles the HTTP body if any. GCDWebServer comes with several subclasses of GCDWebServerRequest to handle common cases like storing the body in memory or stream it to a file on disk.
  • GCDWebServerResponse is created by the request handler and wraps the response HTTP headers and optional body. GCDWebServer comes with several subclasses of GCDWebServerResponse to handle common cases like HTML text in memory or streaming a file from disk.

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:

  • The GCDWebServerMatchBlock is called on every handler added to the GCDWebServer instance whenever a web request has started (i.e. HTTP headers have been received). It is passed the basic info for the web request (HTTP method, URL, headers...) and must decide if it wants to handle it or not. If yes, it must return a new GCDWebServerRequest instance (see above) created with this info. Otherwise, it simply returns nil.
  • The GCDWebServerProcessBlock or GCDWebServerAsyncProcessBlock is called after the web request has been fully received and is passed the GCDWebServerRequest instance created at the previous step. It must return synchronously (if using GCDWebServerProcessBlock) or asynchronously (if using GCDWebServerAsyncProcessBlock) a GCDWebServerResponse instance (see above) or nil on error, which will result in a 500 HTTP status code returned to the client. It's however recommended to return an instance of GCDWebServerErrorResponse on error so more useful information can be returned to the client.

Note that most methods on GCDWebServer to add handlers only require the GCDWebServerProcessBlock or GCDWebServerAsyncProcessBlock 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 GCDWebServerResponse asynchronously. This is achieved by adding handlers that use a GCDWebServerAsyncProcessBlock instead of a GCDWebServerProcessBlock. Here's an example:

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

Hello World

"]; return response; }]; ">
[webServer addDefaultHandlerForMethod:@"GET"
                         requestClass:[GCDWebServerRequest class]
                         processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
  
  GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"

Hello World

"
]; return response; }];

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

Hello World

"]; completionBlock(response); }); }]; ">
[webServer 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())
  dispatch_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:@"

Hello World

"
]; completionBlock(response); }); }];

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

\n", @"Hello World!\n", @"

\n", nil]; // Fake data source we are reading from GCDWebServerStreamedResponse* 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 } }); }]; return response; }]; ">
[webServer addDefaultHandlerForMethod:@"GET"
                         requestClass:[GCDWebServerRequest class]
                         processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
  
  NSMutableArray* contents = [NSMutableArray arrayWithObjects:@"

\n", @"Hello World!\n", @"

\n"
, nil]; // Fake data source we are reading from GCDWebServerStreamedResponse* 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 } }); }]; return 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:

  • GCDWebServer begins a background task whenever the first HTTP connection is opened and ends it only when the last one is closed. This prevents iOS from suspending the app after it goes in the background, which would immediately kill HTTP connections to the client.
  • While the app is in the background, as long as new HTTP connections keep being initiated, the background task will continue to exist and iOS will not suspend the app for up to 10 minutes (unless under sudden and unexpected memory pressure).
  • If the app is still in the background when the last HTTP connection is closed, GCDWebServer will suspend itself and stop accepting new connections as if you had called -stop (this behavior can be disabled with the GCDWebServerOption_AutomaticallySuspendInBackground option).
  • If the app goes in the background while no HTTP connections are opened, GCDWebServer will immediately suspend itself and stop accepting new connections as if you had called -stop (this behavior can be disabled with the GCDWebServerOption_AutomaticallySuspendInBackground option).
  • If the app comes back to the foreground and GCDWebServer had been suspended, it will automatically resume itself and start accepting again new HTTP connections as if you had called -start.

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=1 when compiling GCDWebServer. In Xcode target settings, this can be done by adding DEBUG=1 to the build setting GCC_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):

[self addHandlerForMethod:@"GET"
                     path:@"/"
             requestClass:[GCDWebServerRequest class]
             processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
    
  return [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:

  • The GET handler does not expect any body in the HTTP request and therefore uses the GCDWebServerRequest class. The handler generates a response containing a simple HTML form.
  • The POST handler expects the form values to be in the body of the HTTP request and percent-encoded. Fortunately, GCDWebServer provides the request class GCDWebServerURLEncodedFormRequest which can automatically parse such bodies. The handler simply echoes back the value from the user submitted form.
\
\ Value: \ \
\ \ "; 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:@"

%@

", value]; return [GCDWebServerDataResponse responseWithHTML:html]; }]; ">
[webServer addHandlerForMethod:@"GET"
                          path:@"/"
                  requestClass:[GCDWebServerRequest class]
                  processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
  
  NSString* html = @" \
     \
      
   
\"input\" action=\"/\" method=\"post\" enctype=\"application/x-www-form-urlencoded\"> \ Value: \"text\" name=\"value\"> \ \"submit\" value=\"Submit\"> \ \
\ "; 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:@"

%@

"
, value]; return [GCDWebServerDataResponse responseWithHTML:html]; }];

Advanced Example 3: Serving a Dynamic Website

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];
    
];

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.

Comments
  • Frameworks and Carthage Support

    Frameworks and Carthage Support

    I just added Framework targets for both Mac and iOS.

    I named the Framework GCDWebServers (with an s) because Included frameworks can only #include <> the umbrella header, which means that you can't pick individual headers and since EVERY #include has to be on the umbrella header it would have bloated GCDWebServer.h

    Instead with the GCDWebServers name, it's implicit, that the framework includes all the Servers and we keep a clean GCDWebServer.h

    Now, having framework shared targets in the project, means that this library should support Carthage out of the box (which solves #136) yet for full support, it might be a good idea to provide a prebuilt framework on version releases. It's quite simple so I'll take a look at how to automate that and I'll get back on that later.

    I'm currently working with the Mac version on an App I'm working on, but it might need a little more testing to be sure everything works as it should.

    I'm leaving this as a PR so we don't duplicate efforts with #161 and to be sure you like this approach before investing more time on it :beers:

    opened by sagmor 38
  • -[GCDWebServer bonjourServerURL] should use host name instead of service name

    -[GCDWebServer bonjourServerURL] should use host name instead of service name

    I was confused for a few minutes as to why -[bonjourServerURL] was returning nil. I eventually realized that the service name contained illegal characters, and was not being percent encoded. Should it encode automatically, or throw an exception or assertion?

    I was using the default name, which is the device's name, which I imagine will almost always contain an illegal character.

    bug 
    opened by joeljfischer 34
  • GCDWebServerDelegate receives callback when server is not ready to accept connections

    GCDWebServerDelegate receives callback when server is not ready to accept connections

    So here is the situation, I have a bunch of unit tests that are running with a dependency on GCDWebServer. Here is the ordering of events:

    • Test setup method starts.
    • Singleton class that includes a GCDWebServer object is sent a start method. Singleton class is the delegate for GCDWebServer.
    • Start method on singleton adds handlers and then calls startWithOptions: on the GCDWebServer property.
    • Singleton returns void to caller.
    • Test setup method starts waiting for an NSNotification from the Singleton before continuing.
    • Singleton receives webServerDidStart: method.
    • Singleton sends notification.
    • Test setup method receives notification and continues with test.

    Here are some things I noticed:

    • Tests always pass in Xcode on my (fast) local machine but consistently fail on Travis and will sometimes (but not consistently) fail when run at the command line locally using xcodebuild with xcpretty.
    • The failures are always related to the server not being reachable and look like this: screen shot 2014-04-27 at 12 20 33 pm Note that I am setting the HTTP client base URL using the serverURL property returned by GCDWebServer.
    • Most tellingly I have a test which does the following: Runs the test setups above, then does a simple poll of GCDWebServer on the isRunning property with an assert that it must be true. That test is also failing whenever this happens. screen shot 2014-04-27 at 12 21 00 pm

    Anyway what all this SEEMS to point to is that the delegate is receiving a callback before the server is ready to accept connections. I see the following potential solutions to this problem:

    • Use the method webServerDidCompleteBonjourRegistration: to fire the notification event from the singleton and start the tests. This works but it slows down the tests by several orders of magnitude.
    • Since the isRunning property test is failing I'm assuming it knows that the server is not ready for connections. As such I could use KVO on the isRunning setter property Running but since it looks like the getter is using the ivar _source I suspect that the Running property isn't really being used for setting the isRunning state but I might try it and see or take a harder look at the code.
    • I could introduce a brief wait state after receiving the delegate callback before posting the notification. As you can imagine I REALLY don't like this idea.

    Have you got any suggestions? The runWithOptions: method mentions starting the server synchronously and I could use that instead of the delegate callback but the documentation isn't clear on what the blocking event is (I'll check the code but I imagine its something that might be good to clarify). I also don't see any other delegate callback methods that would work for this (or NSNotification constants) but maybe I'm missing something.

    bug 
    opened by smyrgl 20
  • Unable to reach device with custom Bonjour name

    Unable to reach device with custom Bonjour name

    Changing the name to a custom one when publishing, the device become unreachable. Using the default one instead, the web server provide the content correctly. Any suggestion?

    question 
    opened by pencildrummer 16
  • Crash at [GCDWebServer _endBackgroundTask]

    Crash at [GCDWebServer _endBackgroundTask]

    The web server crashed at

    GCDWebServer.m line 285

    The stack trace can be found on Crashlytics here: http://crashes.to/s/cd6bdc06ca1 The crash is reproducible. It happens during the operations performed in the background process.

    Adding more info:

    
    > [NSThread callStackSymbols]
    [02/12/14 15:41:45] Martino Bonfiglioli: <_NSCallStackArray 0x1a2491f0>(
    0   ???                                 0x01fb2a38 0x0 + 33237560,
    1   musiXmatch                          0x000970a5 main + 0,
    2   libsystem_c.dylib                   0x34f45909 abort + 76,
    3   musiXmatch                          0x0068d3ad -[GCDWebServer _endBackgroundTask] + 548,
    4   musiXmatch                          0x0068d4a5 -[GCDWebServer _didDisconnect] + 244,
    5   musiXmatch                          0x0068ca2d _ConnectedTimerCallBack + 52,
    6   CoreFoundation                      0x26d61c87 <redacted> + 14,
    7   CoreFoundation                      0x26d61803 <redacted> + 650,
    8   CoreFoundation                      0x26d5fa53 <redacted> + 1418,
    9   CoreFoundation                      0x26cad3c1 CFRunLoopRunSpecific + 476,
    10  CoreFoundation                      0x26cad1d3 CFRunLoopRunInMode + 106,
    11  GraphicsServices                    0x2e0510a9 GSEventRunModal + 136,
    12  UIKit                               0x2a2bcfa1 UIApplicationMain + 1440,
    13  musiXmatch                          0x000970d5 main + 48,
    14  libdyld.dylib                       0x34edfaaf <redacted> + 2
    )
    
    and via lldb
    
    

    bt

    • thread #1: tid = 0x306b5c, 0x34fa5dfc libsystem_kernel.dylib__pthread_kill + 8, queue = 'com.apple.main-thread', stop reason = signal SIGABRT frame #0: 0x34fa5dfc libsystem_kernel.dylib__pthread_kill + 8 frame #1: 0x35023d36 libsystem_pthread.dylibpthread_kill + 62 frame #2: 0x34f45908 libsystem_c.dylibabort + 76
      • frame #3: 0x0068d3ac musiXmatch-[GCDWebServer endBackgroundTask](self=0x182d9cc0, cmd=0x2872ed4b) + 548 at GCDWebServer.m:285 frame #4: 0x0068d4a4 musiXmatch-[GCDWebServer didDisconnect](self=0x182d9cc0, cmd=0x319018ff) + 244 at GCDWebServer.m:299 frame #5: 0x0068ca2c musiXmatch_ConnectedTimerCallBack(timer=0x182d9a40, info=0x182d9cc0) + 52 at GCDWebServer.m:180 frame #6: 0x26d61c86 CoreFoundationCFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION + 14 frame #7: 0x26d61802 CoreFoundation__CFRunLoopDoTimer + 650 frame #8: 0x26d5fa52 CoreFoundation__CFRunLoopRun + 1418 frame #9: 0x26cad3c0 CoreFoundationCFRunLoopRunSpecific + 476 frame #10: 0x26cad1d2 CoreFoundationCFRunLoopRunInMode + 106 frame #11: 0x2e0510a8 GraphicsServicesGSEventRunModal + 136 frame #12: 0x2a2bcfa0 UIKitUIApplicationMain + 1440 frame #13: 0x000970d4 musiXmatch`main(argc=1, argv=0x0188caac) + 48 at main.m:12
    bug 
    opened by loretoparisi 14
  • GCDWebServerIsValidByteRange is wrong

    GCDWebServerIsValidByteRange is wrong

    The following code is wrong I suppose the intent was to put an &&

    static inline BOOL GCDWebServerIsValidByteRange(NSRange range) {
      return ((range.location != NSUIntegerMax) || (range.length > 0));
    }
    
    question 
    opened by spearway 12
  • Fixed repeated invocation of block for GCDWebServerStreamedResponse

    Fixed repeated invocation of block for GCDWebServerStreamedResponse

    When attempting to use the GCDWebServerStreamedResponse as described in the README, repeatedly invoking the GCDWebServerBodyReaderCompletionBlock to signal the readiness of more data, a number of bad things happen:

    -The read loop in GCDWebServerConnection writeBodyWithCompletionBlock: repeatedly invokes the body reader completion block attached to the streamed response object (because of a recursive call to writeBodyWithCompletionBlock). I.e. every time the BodyReaderCompletionBlock is called by the asyncStreamBlock, the asyncStreamBlock is invoked again.

    -This leads to a large number of "broken pipe" warnings in the console on iOS8, at least. On a larger project it with many requests it would reliably result in an EXC_BAD_ACCESS, but I have not been able to reproduce this on a small project.

    This can be demonstrated by creating a simple GCDWebServer that does the following:

        self->_server = [[GCDWebServer alloc] init];
        dispatch_time_t half_second = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
    
        [self->_server
            addDefaultHandlerForMethod:@"GET"
            requestClass:[GCDWebServerDataRequest class]
            asyncProcessBlock:^(GCDWebServerRequest *request, GCDWebServerCompletionBlock completionBlock) {
    
                //Wait half a second before sending headers
                dispatch_after(half_second, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
                    GCDWebServerStreamedResponse* res =
                        [[GCDWebServerStreamedResponse alloc]
                            initWithContentType:@"text/plain"
                            asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock dataCompletionBlock) {
    
                                NSLog(@"Sending abcdefg");
                                dataCompletionBlock([@"abcdefg" dataUsingEncoding:NSUTF8StringEncoding], nil);
    
                                dispatch_after(half_second, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
                                    NSLog(@"Sending hijklmnop");
                                    dataCompletionBlock([@"hijklmnop" dataUsingEncoding:NSUTF8StringEncoding], nil);
                                    dataCompletionBlock([[NSData alloc] init], nil);
                                    NSLog(@"ended");
                                });
                        }];
                        completionBlock(res);
                });
        }];
    
        return self;
    

    If you make a single request to this web server, you get the following output:

    [DEBUG] Did open IPv4 listening socket 7
    [DEBUG] Did open IPv6 listening socket 8
    [INFO] GCDWebServer started on port 10001 and reachable at http://192.168.0.14:10001/
    [DEBUG] Did open connection on socket 13
    [DEBUG] Did connect
    [DEBUG] Did start background task
    [DEBUG] Connection received 204 bytes on socket 13
    [DEBUG] Connection on socket 13 preflighting request "GET /something.txt" with 204 bytes body
    [DEBUG] Connection on socket 13 processing request "GET /something.txt" with 204 bytes body
    [DEBUG] Connection sent 176 bytes on socket 13
    2015-01-02 17:57:18.210 gcd_test[4313:89119] Sending abcdefg
    [DEBUG] Connection sent 12 bytes on socket 13
    2015-01-02 17:57:18.212 gcd_test[4313:89118] Sending hijklmnop
    2015-01-02 17:57:18.212 gcd_test[4313:89118] ended
    2015-01-02 17:57:18.212 gcd_test[4313:89119] Sending abcdefg
    [DEBUG] Connection sent 14 bytes on socket 13
    [DEBUG] Connection sent 5 bytes on socket 13
    2015-01-02 17:57:18.212 gcd_test[4313:89118] Sending hijklmnop
    [DEBUG] Connection sent 12 bytes on socket 13
    2015-01-02 17:57:18.212 gcd_test[4313:89133] Sending abcdefg
    2015-01-02 17:57:18.212 gcd_test[4313:89138] Sending abcdefg
    2015-01-02 17:57:18.212 gcd_test[4313:89118] ended
    2015-01-02 17:57:18.212 gcd_test[4313:89133] Sending hijklmnop
    [DEBUG] Connection sent 5 bytes on socket 13
    [DEBUG] Connection sent 14 bytes on socket 13
    2015-01-02 17:57:18.212 gcd_test[4313:89129] Sending hijklmnop
    [DEBUG] Connection sent 12 bytes on socket 13
    2015-01-02 17:57:18.213 gcd_test[4313:89133] ended
    2015-01-02 17:57:18.213 gcd_test[4313:89139] Sending abcdefg
    2015-01-02 17:57:18.213 gcd_test[4313:89129] ended
    [DEBUG] Connection sent 12 bytes on socket 13
    2015-01-02 17:57:18.213 gcd_test[4313:89138] Sending abcdefg
    2015-01-02 17:57:18.213 gcd_test[4313:89118] Sending abcdefg
    [DEBUG] Connection sent 14 bytes on socket 13
    2015-01-02 17:57:18.213 gcd_test[4313:89139] Sending hijklmnop
    2015-01-02 17:57:18.213 gcd_test[4313:89138] Sending hijklmnop
    [DEBUG] Connection sent 5 bytes on socket 13
    [DEBUG] Connection sent 14 bytes on socket 13
    2015-01-02 17:57:18.213 gcd_test[4313:89118] Sending abcdefg
    [DEBUG] Connection sent 5 bytes on socket 13
    2015-01-02 17:57:18.213 gcd_test[4313:89139] ended
    [DEBUG] Connection sent 12 bytes on socket 13
    2015-01-02 17:57:18.213 gcd_test[4313:89133] Sending abcdefg
    2015-01-02 17:57:18.213 gcd_test[4313:89138] ended
    2015-01-02 17:57:18.213 gcd_test[4313:89118] Sending abcdefg
    [DEBUG] Connection sent 12 bytes on socket 13
    2015-01-02 17:57:18.213 gcd_test[4313:89139] Sending hijklmnop
    [DEBUG] Connection sent 12 bytes on socket 13
    [DEBUG] Connection sent 14 bytes on socket 13
    2015-01-02 17:57:18.213 gcd_test[4313:89133] Sending hijklmnop
    2015-01-02 17:57:18.213 gcd_test[4313:89129] Sending hijklmnop
    [DEBUG] Connection sent 5 bytes on socket 13
    [DEBUG] Connection sent 14 bytes on socket 13
    2015-01-02 17:57:18.214 gcd_test[4313:89141] Sending abcdefg
    [DEBUG] Connection sent 5 bytes on socket 13
    2015-01-02 17:57:18.214 gcd_test[4313:89118] Sending abcdefg
    [DEBUG] Connection sent 12 bytes on socket 13
    2015-01-02 17:57:18.214 gcd_test[4313:89139] ended
    [DEBUG] Connection sent 12 bytes on socket 13
    2015-01-02 17:57:18.214 gcd_test[4313:89119] Sending abcdefg
    2015-01-02 17:57:18.214 gcd_test[4313:89133] ended
    2015-01-02 17:57:18.214 gcd_test[4313:89138] Sending abcdefg
    2015-01-02 17:57:18.214 gcd_test[4313:89129] ended
    2015-01-02 17:57:18.214 gcd_test[4313:89143] Sending hijklmnop
    2015-01-02 17:57:18.214 gcd_test[4313:89142] Sending abcdefg
    2015-01-02 17:57:18.214 gcd_test[4313:89118] Sending hijklmnop
    2015-01-02 17:57:18.214 gcd_test[4313:89139] Sending hijklmnop
    2015-01-02 17:57:18.214 gcd_test[4313:89144] Sending abcdefg
    2015-01-02 17:57:18.214 gcd_test[4313:89133] Sending hijklmnop
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.214 gcd_test[4313:89142] Sending hijklmnop
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.214 gcd_test[4313:89129] Sending hijklmnop
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.214 gcd_test[4313:89143] ended
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.214 gcd_test[4313:89118] ended
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.214 gcd_test[4313:89133] ended
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.214 gcd_test[4313:89144] Sending hijklmnop
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.214 gcd_test[4313:89139] ended
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.214 gcd_test[4313:89117] Got alphabet: abcdefghijklmnop
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.215 gcd_test[4313:89129] ended
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.215 gcd_test[4313:89142] ended
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    2015-01-02 17:57:18.215 gcd_test[4313:89144] ended
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [ERROR] Error while writing to socket 13: Broken pipe (32)
    [DEBUG] Did close connection on socket 13
    [VERBOSE] [::1c1e:cd17:0:0:10001] ::5c3:21e:4083:b00:52503 200 "GET /something.txt" (204 | 398)
    [DEBUG] Did disconnect
    [DEBUG] Did end background task
    

    The asyncStreamBlock is called many times even though just one request was made. In this case, the full response is written to the socket before the handler is called a second time, so the worst thing that happens is that a closed socket is written to, but it's possible for the response to become garbage (by having had the same data written to it multiple times), and in a larger project of mine, this problem would reliably cause an EXC_BAD_ACCESS in the GCDWebServerConnection.

    There is a full reproduction project here: https://github.com/KJTsanaktsidis/GCDWebServer-Stream-Issue

    This problem is fundamentially due to the differences in the sync and async body model. For synchronous responses, they are expected to return some data from performDataReadWithCompletion:, and the connection repeatedly calls this method until there is no data left.

    For asynchronous responses, the user of the library can happily invoke the BodyReaderCompletionBlock multiple times until they are done; there is no need for the connection to have a loop driving this process.

    This commit addresses this issue by only repeatedly calling performDataReadWithCompletion: for synchronous response types

    opened by KJTsanaktsidis 12
  • Add support for

    Add support for "If-Range" header

    This header can be either set to the "ETag" or "Last-Modified" value for the entity. If they don't match the current values, the "Range" header should be ignored and the entire resource returned with a 200 status, otherwise only the required byte range should be returned with a 206 status.

    Currently GCDWebServer implements the latter but not the former.

    http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27

    enhancement 
    opened by swisspol 12
  • GCDWebserver stop method crash in iOS 15 (100% reproducibility)

    GCDWebserver stop method crash in iOS 15 (100% reproducibility)

    Application is crashing in background. incident Identifier: 8640BBC5-A99E-4CC6-B6CF-7CDF52DC4DD3 Hardware Model: iPad6,11 Process: [695] Path: /private/var/containers/Bundle/Application/5C74CEF9-26CD-4FBD-AC5D-9B829649385E/.app/ Identifier: com. Version: 4.1.5 (4157) AppStoreTools: 13A227 AppVariant: 1:iPad6,11:13 Code Type: ARM-64 (Native) Role: Foreground Parent Process: launchd [1] Coalition: com.[561]

    Date/Time: 2021-11-21 12:17:35.7839 -0500 Launch Time: 2021-11-21 12:10:28.6341 -0500 OS Version: iPhone OS 15.1 (19B74) Release Type: User Report Version: 104

    Exception Type: EXC_CRASH (SIGKILL) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Triggered by Thread: 0

    Thread 0 name: Thread 0 Crashed: 0 libsystem_kernel.dylib 0x00000001be30a6f4 __ulock_wait + 8 1 libdispatch.dylib 0x000000018510c6ec _dlock_wait + 52 (lock.c:326) 2 libdispatch.dylib 0x000000018510ca7c _dispatch_group_wait_slow + 56 (semaphore.c:214) 3 GCDWebServer 0x000000010189c7a0 -[GCDWebServer _stop] + 296 (GCDWebServer.m:678) 4 GCDWebServer 0x000000010189c9b8 -[GCDWebServer _didEnterBackground:] + 76 (GCDWebServer.m:718) 5 CoreFoundation 0x0000000185436c4c CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER + 20 (CFNotificationCenter.c:652) 6 CoreFoundation 0x00000001854cb554 ___CFXRegistrationPost_block_invoke + 48 (CFNotificationCenter.c:173) 7 CoreFoundation 0x00000001854a0b34 _CFXRegistrationPost + 416 (CFNotificationCenter.c:199) 8 CoreFoundation 0x000000018544b754 _CFXNotificationPost + 696 (CFNotificationCenter.c:1147) 9 Foundation 0x0000000186b66138 -[NSNotificationCenter postNotificationName:object:userInfo:] + 92 (NSNotification.m:560) 10 UIKitCore 0x00000001879c3ce8 __47-[UIApplication _applicationDidEnterBackground]_block_invoke + 312 (UIApplication.m:6972) 11 UIKitCore 0x0000000187926c54 +[UIViewController _performWithoutDeferringTransitionsAllowingAnimation:actions:] + 156 (UIViewController.m:7707) 12 UIKitCore 0x0000000187b4d728 -[UIApplication _applicationDidEnterBackground] + 116 (UIApplication.m:6960) 13 UIKitCore 0x00000001879547c8 __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke_2 + 772 (_UISceneLifecycleMultiplexer.m:696) 14 UIKitCore 0x000000018791002c _UIScenePerformActionsWithLifecycleActionMask + 96 (_UISceneLifecycleState.m:109) 15 UIKitCore 0x0000000187a33b24 __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke + 196 (_UISceneLifecycleMultiplexer.m:565) 16 UIKitCore 0x0000000187b49de4 -[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:] + 240 (_UISceneLifecycleMultiplexer.m:514) 17 UIKitCore 0x0000000187d75554 -[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:] + 732 (_UISceneLifecycleMultiplexer.m:564) 18 UIKitCore 0x0000000187d05448 -[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:] + 336 (_UISceneLifecycleMultiplexer.m:470) 19 UIKitCore 0x0000000187910f14 __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block... + 188 (_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction.m:73) 20 UIKitCore 0x00000001879cce14 +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:actions:completion:] + 812 (BSAnimationSettings+UIKit.m:50) 21 UIKitCore 0x0000000187912ca4 _UISceneSettingsDiffActionPerformChangesWithTransitionContext + 248 (_UISceneSettingsDiffAction.m:23) 22 UIKitCore 0x00000001879f8094 -[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:] + 356 (_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction.m:58) 23 UIKitCore 0x000000018796ccb0 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke.571 + 772 (UIScene.m:1807) 24 UIKitCore 0x0000000187940ff0 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 248 (UIScene.m:1508) 25 UIKitCore 0x0000000187983244 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 264 (UIScene.m:1774) 26 UIKitCore 0x000000018790ef18 -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] + 464 (UIApplicationSceneClientAgent.m:80) 27 FrontBoardServices 0x0000000195f0bdac -[FBSScene updater:didUpdateSettings:withDiff:transitionContext:completion:] + 500 (FBSScene.m:549) 28 FrontBoardServices 0x0000000195f23544 __94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke_2 + 124 (FBSWorkspaceScenesClient.m:581) 29 FrontBoardServices 0x0000000195f09704 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 232 (FBSWorkspace.m:352) 30 FrontBoardServices 0x0000000195f0f4f0 __94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke + 368 (FBSWorkspaceScenesClient.m:580) 31 libdispatch.dylib 0x000000018516c198 _dispatch_client_callout + 16 (object.m:560) 32 libdispatch.dylib 0x000000018510eac0 _dispatch_block_invoke_direct$VARIANT$mp + 220 (queue.c:489) 33 FrontBoardServices 0x0000000195f0ad6c FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK + 40 (FBSSerialQueue.m:157) 34 FrontBoardServices 0x0000000195f0a2f0 -[FBSSerialQueue _targetQueue_performNextIfPossible] + 176 (FBSSerialQueue.m:181) 35 FrontBoardServices 0x0000000195f0e3e0 -[FBSSerialQueue _performNextFromRunLoopSource] + 24 (FBSSerialQueue.m:194) 36 CoreFoundation 0x00000001854c0210 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 24 (CFRunLoop.c:1972) 37 CoreFoundation 0x00000001854d0238 __CFRunLoopDoSource0 + 204 (CFRunLoop.c:2016) 38 CoreFoundation 0x00000001854135d8 __CFRunLoopDoSources0 + 256 (CFRunLoop.c:2053) 39 CoreFoundation 0x0000000185418a08 __CFRunLoopRun + 768 (CFRunLoop.c:2951) 40 CoreFoundation 0x000000018542bd7c CFRunLoopRunSpecific + 572 (CFRunLoop.c:3268) 41 GraphicsServices 0x000000019f6a09a0 GSEventRunModal + 160 (GSEvent.c:2200) 42 UIKitCore 0x0000000187c5e05c -[UIApplication _run] + 1080 (UIApplication.m:3493) 43 UIKitCore 0x00000001879f3ce0 UIApplicationMain + 2028 (UIApplication.m:5046) 44 0x0000000100ad96f8 main + 64 (AppDelegate.swift:14) 45 dyld 0x00000001013f8190 start + 444 (dyldMain.cpp:876)

    opened by rbniranjan 11
  • Allow background mode running for mobile device

    Allow background mode running for mobile device

    At the moment the webserver doesn't allow the webserver to run in the background of a mobile app (which makes sense).

    However, I'm using the webserver when performing UITests for my app. So in that case, the UITests are run on a separate companion app which controls the actual app. So it appears the app is always in the background, but it's running. So to have the webserver embedded in the UITest app, I need it to start even when in backgrounded mode.

    question 
    opened by rhinoid 11
  • Optionally disable debug aborts conditions

    Optionally disable debug aborts conditions

    There are times when GCDWebServer will halt and abort on non-breaking errors. Being able to toggle a flag somewhere would be ideal, rather than just going off of the DEBUG flag.

    question 
    opened by matt-curtis 11
  • Typo on the dynamic website example code

    Typo on the dynamic website example code

    The README.md file has a typo in the code for the "Advanced Example 3: Serving a Dynamic Website" sample code.

    The line:

    pathRegex:@"/.*\.html"

    should be:

    pathRegex:@"/.*\\.html"

    Note the additional backslash needed.

    opened by rmaddy 0
  • Invalid font

    Invalid font

    ITMS-90853: Invalid font - There was an error validating the font at 'Test.app/Frameworks/GCDWebServer.framework/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.woff'. Font not supported by platform.

    opened by rastaman111 2
  • Post Handler Does not work

    Post Handler Does not work

    Get requests to server works and responsive but POST requests gives connection time out for iOS app.

    [DEBUG] Did open connection on socket 11 [DEBUG] Did connect [DEBUG] Connection received 517 bytes on socket 11 [DEBUG] Did start background task [ERROR] No more data available on socket 11 [DEBUG] Connection aborted with status code 500 on socket 11 [DEBUG] Connection sent 116 bytes on socket 11 [DEBUG] Did close connection on socket 11 [VERBOSE] [192.168.1.110:8080] 192.168.1.106:53390 500 "(invalid request)" (517 | 116) [DEBUG] Did disconnect [DEBUG] Did end background task

    Code:

    `webServer.addDefaultHandler(forMethod: "GET", request: GCDWebServerRequest.self, processBlock: {request in

            return GCDWebServerDataResponse(html:"<html><body><p>Hello World</p></body></html>")
                
            })
        
        webServer.addDefaultHandler(forMethod: "POST", request: GCDWebServerURLEncodedFormRequest.self, processBlock: {request in
            return GCDWebServerDataResponse(html:"<html><body><p>Hello World</p></body></html>")     
            })
            
        webServer.start(withPort: 8080, bonjourName: "GCD Web Server")
        
        print("Visit \(webServer.serverURL) in your web browser")
        `
    

    How can I fix this please?

    opened by Semmarl 2
  • The glyphicons-halflings-regular.woff file needs to be removed, otherwise it will not pass the Apple Store review with Xcode14

    The glyphicons-halflings-regular.woff file needs to be removed, otherwise it will not pass the Apple Store review with Xcode14

    Recently we uploaded a package and received an email from Apple, which mentioned: ITMS-90853: Invalid font - There was an error validating the font at 'XXX.app/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.woff'. Font not supported by platform. We removed the relevant files and uploaded a new package, don't get reminder emails anymore.

    opened by ChenPingSheng 1
  • Out of date content is being served after app version upgrade

    Out of date content is being served after app version upgrade

    Hi,

    I am using GCDWebServer in a react-native app to server a static html folder from a bundle.

    I am starting a server using:

    let websiteroot = Bundle.main.path(forResource: "www", ofType: nil)!;
        webServer.addGETHandler(forBasePath: "/", directoryPath: websiteroot, indexFilename: "index.html", cacheAge: 0, allowRangeRequests: false)
    
    try webServer.start(options: [
                GCDWebServerOption_Port: 8080,
                GCDWebServerOption_BindToLocalhost: true,
                GCDWebServerOption_AutomaticallySuspendInBackground: true])
    

    It works fine and serves html correctly up to the point where I update the app (via appstore or manually).

    After update it keeps serving static html content from a previous build instead of a new one.

    Is this expected behaviour? If so is it possible to kill a server and serve new content?

    Currently the only way around it is to uninstall app and install a fresh one in order to receive latest html.

    opened by iamthefox 0
Releases(3.5.4)
  • 3.5.4(Mar 15, 2020)

  • 3.5.3(Aug 9, 2019)

    • Enforce hidden and extensions restrictions when moving and copying files in uploaders
    • Fixed GCDWebServerBodyReaderCompletionBlock not allowing null data
    Source code(tar.gz)
    Source code(zip)
  • 3.5.2(Jan 6, 2019)

  • 3.5.1(Jan 5, 2019)

    • Fixed a path vulnerability traversal in -addGETHandlerForBasePath:, GCDWebUploader, GCDWebDAVServer when using relative paths with .. components (thanks to Antonio Zekic for reporting the issue)
    Source code(tar.gz)
    Source code(zip)
  • 3.5.0(Jan 4, 2019)

  • 3.4.2(Feb 3, 2018)

  • 3.4.1(Aug 21, 2017)

    • Removed support for CocoaLumberJack
    • Fixed CFURLCopyPath() returning NULL for "//" path
    • Return "501 Not implemented" for requests without matching handlers instead of 405
    Source code(tar.gz)
    Source code(zip)
  • 3.4(Jul 3, 2017)

    • Modernized Obj-C including nullability annotations for improved Swift compatibility
    • Allow to customize MIME types used by GCDWebServerFileResponse
    Source code(tar.gz)
    Source code(zip)
  • 3.3.3(Jul 15, 2016)

    • Fixed deflateEnd() being used instead of inflateEnd() in GCDWebServerGZipDecoder
    • Removed exception handling around handlers since it was poorly implemented anyway
    • Fixed a compatibility issue with CocoaPods
    Source code(tar.gz)
    Source code(zip)
  • 3.3.2(Jan 20, 2016)

  • 3.3.1(Jan 16, 2016)

  • 3.3(Dec 15, 2015)

    • Added support for tvOS
    • Added support for NAT port mapping
    • Fixed a crash under Swift 2 related to async handlers (thanks @tifroz)
    • Enable support for Podfiles with use_frameworks! (thanks @macdrevx)
    Source code(tar.gz)
    Source code(zip)
  • 3.2.7(Sep 10, 2015)

  • 3.2.6(Sep 9, 2015)

  • 3.2.5(Jul 3, 2015)

  • 3.2.4(Jun 12, 2015)

  • 3.2.3(Apr 13, 2015)

    • Add remote and local addresses to GCDWebServerRequest (thanks sergiou87)
    • Updated for CocoaLumberJack 2.0
    • Fixed -serverURL not taking into account GCDWebServerOption_BindToLocalhost
    • Handle starting the server with nil options
    Source code(tar.gz)
    Source code(zip)
  • 3.2.2(Jan 15, 2015)

  • 3.2.1(Dec 2, 2014)

  • 3.2(Nov 7, 2014)

  • 3.1(Oct 16, 2014)

  • 3.0.1(Oct 13, 2014)

  • 3.0(Oct 9, 2014)

  • 2.5.3(Oct 7, 2014)

    • Added attributes to GCDWebServerRequest class and GCDWebServerRequestAttribute_RegexCaptures to retrieved captured regex variables
    • Bug fixes
    Source code(tar.gz)
    Source code(zip)
  • 2.5.2(Sep 21, 2014)

    • Improved handling of symbolic links in -addGETHandlerForBasePath:directoryPath:indexFilename:cacheAge:allowRangeRequests:
    • Fixed -bonjourServerURL to correctly return hostname instead of service name
    • Fall back to CFBundleName for GCDWebUploader if CFBundleDisplayName is not available
    Source code(tar.gz)
    Source code(zip)
  • 2.5.1(Aug 24, 2014)

    • Fixed a memory leak sometimes happening when using GCDWebServer through CocoaPods
    • Fixed -isRunning to work as expected when GCDWebServerOption_AutomaticallySuspendInBackground is enabled
    Source code(tar.gz)
    Source code(zip)
  • 2.5(Jul 12, 2014)

    • Validate paths passed to GCDWebDAVServer and GCDWebUploader to ensure they are within the upload directory
    • Reject files greater than 4 GiB in 32 bit mode
    • Now uses NSUIntegerMax instead of NSNotFound to indicate undefined length
    • Bug fixes
    Source code(tar.gz)
    Source code(zip)
  • 2.4(Apr 28, 2014)

    • Modified GCDWebServerMultiPart to allow duplicate control names
    • Added support for "multipart/mixed" parts inside "multipart/form-data"
    • Added "error" argument to -startWithOptions:
    • Fixed listening socket not guaranteed to be closed when -stop returns
    Source code(tar.gz)
    Source code(zip)
  • 2.3.2(Apr 23, 2014)

  • 2.3.1(Apr 19, 2014)

    • Added documentation to headers
    • Added -webServerDidCompleteBonjourRegistration:
    • Added -logException:
    • Renamed showHiddenFiles property to allowHiddenItems in GCDWebUploader and GCDWebDAVServer
    • Renamed GCDWebServerStreamingResponse to GCDWebServerStreamedResponse
    • Fixed a caching issue with -addGETHandlerForPath:staticData:contentType:cacheAge:
    Source code(tar.gz)
    Source code(zip)
Owner
Pierre-Olivier Latour
Pierre-Olivier Latour
A small, lightweight, embeddable HTTP server for Mac OS X or iOS applications

CocoaHTTPServer CocoaHTTPServer is a small, lightweight, embeddable HTTP server for Mac OS X or iOS applications. Sometimes developers need an embedde

Robbie Hanson 5.5k Jan 7, 2023
Lightweight library for web server applications in Swift on macOS and Linux powered by coroutines.

Why Zewo? • Support • Community • Contributing Zewo Zewo is a lightweight library for web applications in Swift. What sets Zewo apart? Zewo is not a w

Zewo 1.9k Dec 22, 2022
A Swift web framework and HTTP server.

A Swift Web Framework and HTTP Server Summary Kitura is a web framework and web server that is created for web services written in Swift. For more inf

Kitura 7.6k Dec 27, 2022
Tiny http server engine written in Swift programming language.

What is Swifter? Tiny http server engine written in Swift programming language. Branches * stable - lands on CocoaPods and others. Supports the latest

null 3.6k Dec 31, 2022
Swift HTTP server using the pre-fork worker model

Curassow Curassow is a Swift Nest HTTP Server. It uses the pre-fork worker model and it's similar to Python's Gunicorn and Ruby's Unicorn. It exposes

Kyle Fuller Archive 397 Oct 30, 2022
💧 A server-side Swift HTTP web framework.

Vapor is an HTTP web framework for Swift. It provides a beautifully expressive and easy-to-use foundation for your next website, API, or cloud project

Vapor 22.4k Jan 3, 2023
libuv base Swift web HTTP server framework

Notice Trevi now open a Trevi Community. Yoseob/Trevi project split up into respective Trevi, lime, middlewares and sys packages at our community. If

leeyoseob 46 Jan 29, 2022
Tiny http server engine written in Swift programming language.

What is Swifter? Tiny http server engine written in Swift programming language. Branches * stable - lands on CocoaPods and others. Supports the latest

null 3.6k Jan 5, 2023
A lightweight library for writing HTTP web servers with Swift

Taylor Disclaimer: Not actively working on it anymore. You can check out some alternatives Swift 2.0 required. Working with Xcode 7.1. Disclaimer: It

Jorge Izquierdo 925 Nov 17, 2022
🪶 Feather is a modern Swift-based content management system powered by Vapor 4.

Feather CMS ?? ?? Feather is a modern Swift-based content management system powered by Vapor 4. ?? Click to join the chat on Discord. Requirements To

Phu Huynh 0 Oct 20, 2021
PillowTalk - An iOS & SwiftUI server monitor tool for linux based machines using remote proc file system with script execution.

An iOS & SwiftUI server monitor tool for linux based machines using remote proc file system with script execution.

Lakr Aream 416 Dec 16, 2022
HTTP Implementation for Swift on Linux and Mac OS X

Swift HTTP Server Simple HTTP implementation for Swift using POSIX socket API. Running on Mac OS X and Linux. For Mac users: You can install new Swift

Huy 451 Jul 28, 2022
Server-side Swift. The Perfect core toolset and framework for Swift Developers. (For mobile back-end development, website and API development, and more…)

Perfect: Server-Side Swift 简体中文 Perfect: Server-Side Swift Perfect is a complete and powerful toolbox, framework, and application server for Linux, iO

PerfectlySoft Inc. 13.9k Dec 29, 2022
iOS Tweak to redirect Discord API calls to a Fosscord server.

FosscordTweak iOS Tweak to redirect Discord API calls to a Fosscord server. Installation Manual Download .deb file from release and install on jailbro

null 8 May 16, 2022
Simple server APIs in Swift

Simple server APIs in Swift

null 4 Apr 25, 2022
Swift Express is a simple, yet unopinionated web application server written in Swift

Documentation <h5 align="right"><a href="http://demo.swiftexpress.io/">Live ?? server running Demo <img src="https://cdn0.iconfinder.com/data/icons/

Crossroad Labs 850 Dec 2, 2022
High Performance (nearly)100% Swift Web server supporting dynamic content.

Dynamo - Dynamic Swift Web Server Starting this project the intention was to code the simplest possible Web Server entirely in Swift. Unfortunately I

John Holdsworth 68 Jul 25, 2022
Swift backend / server framework (Pure Swift, Supports Linux)

NetworkObjects NetworkObjects is a #PureSwift backend. This framework compiles for OS X, iOS and Linux and serves as the foundation for building power

Alsey Coleman Miller 258 Oct 6, 2022
Reliable Server Side Swift ✭ Make Apache great again!

mod_swift mod_swift is a technology demo which shows how to write native modules for the Apache Web Server in the Swift 3 programming language. The de

The ApacheExpress Alliance 174 Oct 22, 2022