├── .DS_Store ├── .gitignore ├── GCDWebServerDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── GCDWebServerDemo ├── .DS_Store ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── GCDWebDAVServer │ ├── GCDWebDAVServer.h │ └── GCDWebDAVServer.m ├── GCDWebServer │ ├── .DS_Store │ ├── Core │ │ ├── GCDWebServer.h │ │ ├── GCDWebServer.m │ │ ├── GCDWebServerConnection.h │ │ ├── GCDWebServerConnection.m │ │ ├── GCDWebServerFunctions.h │ │ ├── GCDWebServerFunctions.m │ │ ├── GCDWebServerHTTPStatusCodes.h │ │ ├── GCDWebServerPrivate.h │ │ ├── GCDWebServerRequest.h │ │ ├── GCDWebServerRequest.m │ │ ├── GCDWebServerResponse.h │ │ └── GCDWebServerResponse.m │ ├── Requests │ │ ├── GCDWebServerDataRequest.h │ │ ├── GCDWebServerDataRequest.m │ │ ├── GCDWebServerFileRequest.h │ │ ├── GCDWebServerFileRequest.m │ │ ├── GCDWebServerMultiPartFormRequest.h │ │ ├── GCDWebServerMultiPartFormRequest.m │ │ ├── GCDWebServerURLEncodedFormRequest.h │ │ └── GCDWebServerURLEncodedFormRequest.m │ └── Responses │ │ ├── GCDWebServerDataResponse.h │ │ ├── GCDWebServerDataResponse.m │ │ ├── GCDWebServerErrorResponse.h │ │ ├── GCDWebServerErrorResponse.m │ │ ├── GCDWebServerFileResponse.h │ │ ├── GCDWebServerFileResponse.m │ │ ├── GCDWebServerStreamedResponse.h │ │ └── GCDWebServerStreamedResponse.m ├── GCDWebUploader │ ├── GCDWebUploader.bundle │ │ └── Contents │ │ │ ├── Info.plist │ │ │ └── Resources │ │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap.css │ │ │ ├── index.css │ │ │ └── jquery.fileupload.css │ │ │ ├── en.lproj │ │ │ └── Localizable.strings │ │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ │ ├── index.html │ │ │ └── js │ │ │ ├── bootstrap.min.js │ │ │ ├── html5shiv.min.js │ │ │ ├── index.js │ │ │ ├── jquery.fileupload.js │ │ │ ├── jquery.iframe-transport.js │ │ │ ├── jquery.jeditable.js │ │ │ ├── jquery.min.js │ │ │ ├── jquery.ui.widget.js │ │ │ ├── respond.min.js │ │ │ └── tmpl.min.js │ ├── GCDWebUploader.h │ └── GCDWebUploader.m ├── Info.plist ├── SJXCSMIPHelper.h ├── SJXCSMIPHelper.m ├── ViewController.h ├── ViewController.m └── main.m ├── LICENSE └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjxjjx/GCDWebServerDemo/e6cee9613e6ebcf3866093959d2ead7ebbb1cbea/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /GCDWebServerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GCDWebServerDemo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjxjjx/GCDWebServerDemo/e6cee9613e6ebcf3866093959d2ead7ebbb1cbea/GCDWebServerDemo/.DS_Store -------------------------------------------------------------------------------- /GCDWebServerDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // GCDWebServerDemo 4 | // 5 | // Created by shapp on 2017/8/23. 6 | // Copyright © 2017年 shapp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /GCDWebServerDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // GCDWebServerDemo 4 | // 5 | // Created by shapp on 2017/8/23. 6 | // Copyright © 2017年 shapp. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /GCDWebServerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /GCDWebServerDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /GCDWebServerDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebDAVServer/GCDWebDAVServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServer.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | @class GCDWebDAVServer; 33 | 34 | /** 35 | * Delegate methods for GCDWebDAVServer. 36 | * 37 | * @warning These methods are always called on the main thread in a serialized way. 38 | */ 39 | @protocol GCDWebDAVServerDelegate 40 | @optional 41 | 42 | /** 43 | * This method is called whenever a file has been downloaded. 44 | */ 45 | - (void)davServer:(GCDWebDAVServer*)server didDownloadFileAtPath:(NSString*)path; 46 | 47 | /** 48 | * This method is called whenever a file has been uploaded. 49 | */ 50 | - (void)davServer:(GCDWebDAVServer*)server didUploadFileAtPath:(NSString*)path; 51 | 52 | /** 53 | * This method is called whenever a file or directory has been moved. 54 | */ 55 | - (void)davServer:(GCDWebDAVServer*)server didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; 56 | 57 | /** 58 | * This method is called whenever a file or directory has been copied. 59 | */ 60 | - (void)davServer:(GCDWebDAVServer*)server didCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; 61 | 62 | /** 63 | * This method is called whenever a file or directory has been deleted. 64 | */ 65 | - (void)davServer:(GCDWebDAVServer*)server didDeleteItemAtPath:(NSString*)path; 66 | 67 | /** 68 | * This method is called whenever a directory has been created. 69 | */ 70 | - (void)davServer:(GCDWebDAVServer*)server didCreateDirectoryAtPath:(NSString*)path; 71 | 72 | @end 73 | 74 | /** 75 | * The GCDWebDAVServer subclass of GCDWebServer implements a class 1 compliant 76 | * WebDAV server. It is also partially class 2 compliant but only when the 77 | * client is the OS X WebDAV implementation (so it can work with the OS X Finder). 78 | * 79 | * See the README.md file for more information about the features of GCDWebDAVServer. 80 | */ 81 | @interface GCDWebDAVServer : GCDWebServer 82 | 83 | /** 84 | * Returns the upload directory as specified when the server was initialized. 85 | */ 86 | @property(nonatomic, readonly) NSString* uploadDirectory; 87 | 88 | /** 89 | * Sets the delegate for the server. 90 | */ 91 | @property(nonatomic, weak, nullable) id delegate; 92 | 93 | /** 94 | * Sets which files are allowed to be operated on depending on their extension. 95 | * 96 | * The default value is nil i.e. all file extensions are allowed. 97 | */ 98 | @property(nonatomic, copy) NSArray* allowedFileExtensions; 99 | 100 | /** 101 | * Sets if files and directories whose name start with a period are allowed to 102 | * be operated on. 103 | * 104 | * The default value is NO. 105 | */ 106 | @property(nonatomic) BOOL allowHiddenItems; 107 | 108 | /** 109 | * This method is the designated initializer for the class. 110 | */ 111 | - (instancetype)initWithUploadDirectory:(NSString*)path; 112 | 113 | @end 114 | 115 | /** 116 | * Hooks to customize the behavior of GCDWebDAVServer. 117 | * 118 | * @warning These methods can be called on any GCD thread. 119 | */ 120 | @interface GCDWebDAVServer (Subclassing) 121 | 122 | /** 123 | * This method is called to check if a file upload is allowed to complete. 124 | * The uploaded file is available for inspection at "tempPath". 125 | * 126 | * The default implementation returns YES. 127 | */ 128 | - (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath; 129 | 130 | /** 131 | * This method is called to check if a file or directory is allowed to be moved. 132 | * 133 | * The default implementation returns YES. 134 | */ 135 | - (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; 136 | 137 | /** 138 | * This method is called to check if a file or directory is allowed to be copied. 139 | * 140 | * The default implementation returns YES. 141 | */ 142 | - (BOOL)shouldCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; 143 | 144 | /** 145 | * This method is called to check if a file or directory is allowed to be deleted. 146 | * 147 | * The default implementation returns YES. 148 | */ 149 | - (BOOL)shouldDeleteItemAtPath:(NSString*)path; 150 | 151 | /** 152 | * This method is called to check if a directory is allowed to be created. 153 | * 154 | * The default implementation returns YES. 155 | */ 156 | - (BOOL)shouldCreateDirectoryAtPath:(NSString*)path; 157 | 158 | @end 159 | 160 | NS_ASSUME_NONNULL_END 161 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjxjjx/GCDWebServerDemo/e6cee9613e6ebcf3866093959d2ead7ebbb1cbea/GCDWebServerDemo/GCDWebServer/.DS_Store -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerConnection.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServer.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | @class GCDWebServerHandler; 33 | 34 | /** 35 | * The GCDWebServerConnection class is instantiated by GCDWebServer to handle 36 | * each new HTTP connection. Each instance stays alive until the connection is 37 | * closed. 38 | * 39 | * You cannot use this class directly, but it is made public so you can 40 | * subclass it to override some hooks. Use the GCDWebServerOption_ConnectionClass 41 | * option for GCDWebServer to install your custom subclass. 42 | * 43 | * @warning The GCDWebServerConnection retains the GCDWebServer until the 44 | * connection is closed. 45 | */ 46 | @interface GCDWebServerConnection : NSObject 47 | 48 | /** 49 | * Returns the GCDWebServer that owns the connection. 50 | */ 51 | @property(nonatomic, readonly) GCDWebServer* server; 52 | 53 | /** 54 | * Returns YES if the connection is using IPv6. 55 | */ 56 | @property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6; 57 | 58 | /** 59 | * Returns the address of the local peer (i.e. server) of the connection 60 | * as a raw "struct sockaddr". 61 | */ 62 | @property(nonatomic, readonly) NSData* localAddressData; 63 | 64 | /** 65 | * Returns the address of the local peer (i.e. server) of the connection 66 | * as a string. 67 | */ 68 | @property(nonatomic, readonly) NSString* localAddressString; 69 | 70 | /** 71 | * Returns the address of the remote peer (i.e. client) of the connection 72 | * as a raw "struct sockaddr". 73 | */ 74 | @property(nonatomic, readonly) NSData* remoteAddressData; 75 | 76 | /** 77 | * Returns the address of the remote peer (i.e. client) of the connection 78 | * as a string. 79 | */ 80 | @property(nonatomic, readonly) NSString* remoteAddressString; 81 | 82 | /** 83 | * Returns the total number of bytes received from the remote peer (i.e. client) 84 | * so far. 85 | */ 86 | @property(nonatomic, readonly) NSUInteger totalBytesRead; 87 | 88 | /** 89 | * Returns the total number of bytes sent to the remote peer (i.e. client) so far. 90 | */ 91 | @property(nonatomic, readonly) NSUInteger totalBytesWritten; 92 | 93 | @end 94 | 95 | /** 96 | * Hooks to customize the behavior of GCDWebServer HTTP connections. 97 | * 98 | * @warning These methods can be called on any GCD thread. 99 | * Be sure to also call "super" when overriding them. 100 | */ 101 | @interface GCDWebServerConnection (Subclassing) 102 | 103 | /** 104 | * This method is called when the connection is opened. 105 | * 106 | * Return NO to reject the connection e.g. after validating the local 107 | * or remote address. 108 | */ 109 | - (BOOL)open; 110 | 111 | /** 112 | * This method is called whenever data has been received 113 | * from the remote peer (i.e. client). 114 | * 115 | * @warning Do not attempt to modify this data. 116 | */ 117 | - (void)didReadBytes:(const void*)bytes length:(NSUInteger)length; 118 | 119 | /** 120 | * This method is called whenever data has been sent 121 | * to the remote peer (i.e. client). 122 | * 123 | * @warning Do not attempt to modify this data. 124 | */ 125 | - (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length; 126 | 127 | /** 128 | * This method is called after the HTTP headers have been received to 129 | * allow replacing the request URL by another one. 130 | * 131 | * The default implementation returns the original URL. 132 | */ 133 | - (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers; 134 | 135 | /** 136 | * Assuming a valid HTTP request was received, this method is called before 137 | * the request is processed. 138 | * 139 | * Return a non-nil GCDWebServerResponse to bypass the request processing entirely. 140 | * 141 | * The default implementation checks for HTTP authentication if applicable 142 | * and returns a barebone 401 status code response if authentication failed. 143 | */ 144 | - (nullable GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request; 145 | 146 | /** 147 | * Assuming a valid HTTP request was received and -preflightRequest: returned nil, 148 | * this method is called to process the request by executing the handler's 149 | * process block. 150 | */ 151 | - (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion; 152 | 153 | /** 154 | * Assuming a valid HTTP request was received and either -preflightRequest: 155 | * or -processRequest:completion: returned a non-nil GCDWebServerResponse, 156 | * this method is called to override the response. 157 | * 158 | * You can either modify the current response and return it, or return a 159 | * completely new one. 160 | * 161 | * The default implementation replaces any response matching the "ETag" or 162 | * "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304) 163 | * one. 164 | */ 165 | - (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request; 166 | 167 | /** 168 | * This method is called if any error happens while validing or processing 169 | * the request or if no GCDWebServerResponse was generated during processing. 170 | * 171 | * @warning If the request was invalid (e.g. the HTTP headers were malformed), 172 | * the "request" argument will be nil. 173 | */ 174 | - (void)abortRequest:(nullable GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode; 175 | 176 | /** 177 | * Called when the connection is closed. 178 | */ 179 | - (void)close; 180 | 181 | @end 182 | 183 | NS_ASSUME_NONNULL_END 184 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerFunctions.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | /** 37 | * Converts a file extension to the corresponding MIME type. 38 | * If there is no match, "application/octet-stream" is returned. 39 | * 40 | * Overrides allow to customize the built-in mapping from extensions to MIME 41 | * types. Keys of the dictionary must be lowercased file extensions without 42 | * the period, and the values must be the corresponding MIME types. 43 | */ 44 | NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* _Nullable overrides); 45 | 46 | /** 47 | * Add percent-escapes to a string so it can be used in a URL. 48 | * The legal characters ":@/?&=+" are also escaped to ensure compatibility 49 | * with URL encoded forms and URL queries. 50 | */ 51 | NSString* _Nullable GCDWebServerEscapeURLString(NSString* string); 52 | 53 | /** 54 | * Unescapes a URL percent-encoded string. 55 | */ 56 | NSString* _Nullable GCDWebServerUnescapeURLString(NSString* string); 57 | 58 | /** 59 | * Extracts the unescaped names and values from an 60 | * "application/x-www-form-urlencoded" form. 61 | * http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 62 | */ 63 | NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); 64 | 65 | /** 66 | * On OS X, returns the IPv4 or IPv6 address as a string of the primary 67 | * connected service or nil if not available. 68 | * 69 | * On iOS, returns the IPv4 or IPv6 address as a string of the WiFi 70 | * interface if connected or nil otherwise. 71 | */ 72 | NSString* _Nullable GCDWebServerGetPrimaryIPAddress(BOOL useIPv6); 73 | 74 | /** 75 | * Converts a date into a string using RFC822 formatting. 76 | * https://tools.ietf.org/html/rfc822#section-5 77 | * https://tools.ietf.org/html/rfc1123#section-5.2.14 78 | */ 79 | NSString* GCDWebServerFormatRFC822(NSDate* date); 80 | 81 | /** 82 | * Converts a RFC822 formatted string into a date. 83 | * https://tools.ietf.org/html/rfc822#section-5 84 | * https://tools.ietf.org/html/rfc1123#section-5.2.14 85 | * 86 | * @warning Timezones other than GMT are not supported by this function. 87 | */ 88 | NSDate* _Nullable GCDWebServerParseRFC822(NSString* string); 89 | 90 | /** 91 | * Converts a date into a string using IOS 8601 formatting. 92 | * http://tools.ietf.org/html/rfc3339#section-5.6 93 | */ 94 | NSString* GCDWebServerFormatISO8601(NSDate* date); 95 | 96 | /** 97 | * Converts a ISO 8601 formatted string into a date. 98 | * http://tools.ietf.org/html/rfc3339#section-5.6 99 | * 100 | * @warning Only "calendar" variant is supported at this time and timezones 101 | * other than GMT are not supported either. 102 | */ 103 | NSDate* _Nullable GCDWebServerParseISO8601(NSString* string); 104 | 105 | #ifdef __cplusplus 106 | } 107 | #endif 108 | 109 | NS_ASSUME_NONNULL_END 110 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerFunctions.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | #if TARGET_OS_IPHONE 34 | #import 35 | #else 36 | #import 37 | #endif 38 | #import 39 | 40 | #import 41 | #import 42 | #import 43 | 44 | #import "GCDWebServerPrivate.h" 45 | 46 | static NSDateFormatter* _dateFormatterRFC822 = nil; 47 | static NSDateFormatter* _dateFormatterISO8601 = nil; 48 | static dispatch_queue_t _dateFormatterQueue = NULL; 49 | 50 | // TODO: Handle RFC 850 and ANSI C's asctime() format 51 | void GCDWebServerInitializeFunctions() { 52 | GWS_DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread 53 | if (_dateFormatterRFC822 == nil) { 54 | _dateFormatterRFC822 = [[NSDateFormatter alloc] init]; 55 | _dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; 56 | _dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'"; 57 | _dateFormatterRFC822.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; 58 | GWS_DCHECK(_dateFormatterRFC822); 59 | } 60 | if (_dateFormatterISO8601 == nil) { 61 | _dateFormatterISO8601 = [[NSDateFormatter alloc] init]; 62 | _dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; 63 | _dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'"; 64 | _dateFormatterISO8601.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; 65 | GWS_DCHECK(_dateFormatterISO8601); 66 | } 67 | if (_dateFormatterQueue == NULL) { 68 | _dateFormatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 69 | GWS_DCHECK(_dateFormatterQueue); 70 | } 71 | } 72 | 73 | NSString* GCDWebServerNormalizeHeaderValue(NSString* value) { 74 | if (value) { 75 | NSRange range = [value rangeOfString:@";"]; // Assume part before ";" separator is case-insensitive 76 | if (range.location != NSNotFound) { 77 | value = [[[value substringToIndex:range.location] lowercaseString] stringByAppendingString:[value substringFromIndex:range.location]]; 78 | } else { 79 | value = [value lowercaseString]; 80 | } 81 | } 82 | return value; 83 | } 84 | 85 | NSString* GCDWebServerTruncateHeaderValue(NSString* value) { 86 | if (value) { 87 | NSRange range = [value rangeOfString:@";"]; 88 | if (range.location != NSNotFound) { 89 | return [value substringToIndex:range.location]; 90 | } 91 | } 92 | return value; 93 | } 94 | 95 | NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) { 96 | NSString* parameter = nil; 97 | if (value) { 98 | NSScanner* scanner = [[NSScanner alloc] initWithString:value]; 99 | [scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive 100 | NSString* string = [NSString stringWithFormat:@"%@=", name]; 101 | if ([scanner scanUpToString:string intoString:NULL]) { 102 | [scanner scanString:string intoString:NULL]; 103 | if ([scanner scanString:@"\"" intoString:NULL]) { 104 | [scanner scanUpToString:@"\"" intoString:¶meter]; 105 | } else { 106 | [scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter]; 107 | } 108 | } 109 | } 110 | return parameter; 111 | } 112 | 113 | // http://www.w3schools.com/tags/ref_charactersets.asp 114 | NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) { 115 | NSStringEncoding encoding = kCFStringEncodingInvalidId; 116 | if (charset) { 117 | encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset)); 118 | } 119 | return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding); 120 | } 121 | 122 | NSString* GCDWebServerFormatRFC822(NSDate* date) { 123 | __block NSString* string; 124 | dispatch_sync(_dateFormatterQueue, ^{ 125 | string = [_dateFormatterRFC822 stringFromDate:date]; 126 | }); 127 | return string; 128 | } 129 | 130 | NSDate* GCDWebServerParseRFC822(NSString* string) { 131 | __block NSDate* date; 132 | dispatch_sync(_dateFormatterQueue, ^{ 133 | date = [_dateFormatterRFC822 dateFromString:string]; 134 | }); 135 | return date; 136 | } 137 | 138 | NSString* GCDWebServerFormatISO8601(NSDate* date) { 139 | __block NSString* string; 140 | dispatch_sync(_dateFormatterQueue, ^{ 141 | string = [_dateFormatterISO8601 stringFromDate:date]; 142 | }); 143 | return string; 144 | } 145 | 146 | NSDate* GCDWebServerParseISO8601(NSString* string) { 147 | __block NSDate* date; 148 | dispatch_sync(_dateFormatterQueue, ^{ 149 | date = [_dateFormatterISO8601 dateFromString:string]; 150 | }); 151 | return date; 152 | } 153 | 154 | BOOL GCDWebServerIsTextContentType(NSString* type) { 155 | return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]); 156 | } 157 | 158 | NSString* GCDWebServerDescribeData(NSData* data, NSString* type) { 159 | if (GCDWebServerIsTextContentType(type)) { 160 | NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset"); 161 | NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 162 | if (string) { 163 | return string; 164 | } 165 | } 166 | return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length]; 167 | } 168 | 169 | NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* overrides) { 170 | NSDictionary* builtInOverrides = @{ @"css" : @"text/css" }; 171 | NSString* mimeType = nil; 172 | extension = [extension lowercaseString]; 173 | if (extension.length) { 174 | mimeType = [overrides objectForKey:extension]; 175 | if (mimeType == nil) { 176 | mimeType = [builtInOverrides objectForKey:extension]; 177 | } 178 | if (mimeType == nil) { 179 | CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); 180 | if (uti) { 181 | mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)); 182 | CFRelease(uti); 183 | } 184 | } 185 | } 186 | return mimeType ? mimeType : kGCDWebServerDefaultMimeType; 187 | } 188 | 189 | NSString* GCDWebServerEscapeURLString(NSString* string) { 190 | #pragma clang diagnostic push 191 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 192 | return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8)); 193 | #pragma clang diagnostic pop 194 | } 195 | 196 | NSString* GCDWebServerUnescapeURLString(NSString* string) { 197 | #pragma clang diagnostic push 198 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 199 | return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8)); 200 | #pragma clang diagnostic pop 201 | } 202 | 203 | NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { 204 | NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; 205 | NSScanner* scanner = [[NSScanner alloc] initWithString:form]; 206 | [scanner setCharactersToBeSkipped:nil]; 207 | while (1) { 208 | NSString* key = nil; 209 | if (![scanner scanUpToString:@"=" intoString:&key] || [scanner isAtEnd]) { 210 | break; 211 | } 212 | [scanner setScanLocation:([scanner scanLocation] + 1)]; 213 | 214 | NSString* value = nil; 215 | [scanner scanUpToString:@"&" intoString:&value]; 216 | if (value == nil) { 217 | value = @""; 218 | } 219 | 220 | key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "]; 221 | NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil; 222 | value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; 223 | NSString* unescapedValue = value ? GCDWebServerUnescapeURLString(value) : nil; 224 | if (unescapedKey && unescapedValue) { 225 | [parameters setObject:unescapedValue forKey:unescapedKey]; 226 | } else { 227 | GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value); 228 | GWS_DNOT_REACHED(); 229 | } 230 | 231 | if ([scanner isAtEnd]) { 232 | break; 233 | } 234 | [scanner setScanLocation:([scanner scanLocation] + 1)]; 235 | } 236 | return parameters; 237 | } 238 | 239 | NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService) { 240 | char hostBuffer[NI_MAXHOST]; 241 | char serviceBuffer[NI_MAXSERV]; 242 | if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) != 0) { 243 | #if DEBUG 244 | GWS_DNOT_REACHED(); 245 | #else 246 | return @""; 247 | #endif 248 | } 249 | return includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : (NSString*)[NSString stringWithUTF8String:hostBuffer]; 250 | } 251 | 252 | NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) { 253 | NSString* address = nil; 254 | #if TARGET_OS_IPHONE 255 | #if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_TV 256 | const char* primaryInterface = "en0"; // WiFi interface on iOS 257 | #endif 258 | #else 259 | const char* primaryInterface = NULL; 260 | SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL); 261 | if (store) { 262 | CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same 263 | if (info) { 264 | NSString* interface = [(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"]; 265 | if (interface) { 266 | primaryInterface = [[NSString stringWithString:interface] UTF8String]; // Copy string to auto-release pool 267 | } 268 | CFRelease(info); 269 | } 270 | CFRelease(store); 271 | } 272 | if (primaryInterface == NULL) { 273 | primaryInterface = "lo0"; 274 | } 275 | #endif 276 | struct ifaddrs* list; 277 | if (getifaddrs(&list) >= 0) { 278 | for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) { 279 | #if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV 280 | // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator 281 | // Assumption holds for Apple TV running tvOS 282 | if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) 283 | #else 284 | if (strcmp(ifap->ifa_name, primaryInterface)) 285 | #endif 286 | { 287 | continue; 288 | } 289 | if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) { 290 | address = GCDWebServerStringFromSockAddr(ifap->ifa_addr, NO); 291 | break; 292 | } 293 | } 294 | freeifaddrs(list); 295 | } 296 | return address; 297 | } 298 | 299 | NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) { 300 | va_list arguments; 301 | va_start(arguments, format); 302 | const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String]; 303 | va_end(arguments); 304 | unsigned char md5[CC_MD5_DIGEST_LENGTH]; 305 | CC_MD5(string, (CC_LONG)strlen(string), md5); 306 | char buffer[2 * CC_MD5_DIGEST_LENGTH + 1]; 307 | for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) { 308 | unsigned char byte = md5[i]; 309 | unsigned char byteHi = (byte & 0xF0) >> 4; 310 | buffer[2 * i + 0] = byteHi >= 10 ? 'a' + byteHi - 10 : '0' + byteHi; 311 | unsigned char byteLo = byte & 0x0F; 312 | buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo; 313 | } 314 | buffer[2 * CC_MD5_DIGEST_LENGTH] = 0; 315 | return (NSString*)[NSString stringWithUTF8String:buffer]; 316 | } 317 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 29 | // http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml 30 | 31 | #import 32 | 33 | /** 34 | * Convenience constants for "informational" HTTP status codes. 35 | */ 36 | typedef NS_ENUM(NSInteger, GCDWebServerInformationalHTTPStatusCode) { 37 | kGCDWebServerHTTPStatusCode_Continue = 100, 38 | kGCDWebServerHTTPStatusCode_SwitchingProtocols = 101, 39 | kGCDWebServerHTTPStatusCode_Processing = 102 40 | }; 41 | 42 | /** 43 | * Convenience constants for "successful" HTTP status codes. 44 | */ 45 | typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) { 46 | kGCDWebServerHTTPStatusCode_OK = 200, 47 | kGCDWebServerHTTPStatusCode_Created = 201, 48 | kGCDWebServerHTTPStatusCode_Accepted = 202, 49 | kGCDWebServerHTTPStatusCode_NonAuthoritativeInformation = 203, 50 | kGCDWebServerHTTPStatusCode_NoContent = 204, 51 | kGCDWebServerHTTPStatusCode_ResetContent = 205, 52 | kGCDWebServerHTTPStatusCode_PartialContent = 206, 53 | kGCDWebServerHTTPStatusCode_MultiStatus = 207, 54 | kGCDWebServerHTTPStatusCode_AlreadyReported = 208 55 | }; 56 | 57 | /** 58 | * Convenience constants for "redirection" HTTP status codes. 59 | */ 60 | typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) { 61 | kGCDWebServerHTTPStatusCode_MultipleChoices = 300, 62 | kGCDWebServerHTTPStatusCode_MovedPermanently = 301, 63 | kGCDWebServerHTTPStatusCode_Found = 302, 64 | kGCDWebServerHTTPStatusCode_SeeOther = 303, 65 | kGCDWebServerHTTPStatusCode_NotModified = 304, 66 | kGCDWebServerHTTPStatusCode_UseProxy = 305, 67 | kGCDWebServerHTTPStatusCode_TemporaryRedirect = 307, 68 | kGCDWebServerHTTPStatusCode_PermanentRedirect = 308 69 | }; 70 | 71 | /** 72 | * Convenience constants for "client error" HTTP status codes. 73 | */ 74 | typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) { 75 | kGCDWebServerHTTPStatusCode_BadRequest = 400, 76 | kGCDWebServerHTTPStatusCode_Unauthorized = 401, 77 | kGCDWebServerHTTPStatusCode_PaymentRequired = 402, 78 | kGCDWebServerHTTPStatusCode_Forbidden = 403, 79 | kGCDWebServerHTTPStatusCode_NotFound = 404, 80 | kGCDWebServerHTTPStatusCode_MethodNotAllowed = 405, 81 | kGCDWebServerHTTPStatusCode_NotAcceptable = 406, 82 | kGCDWebServerHTTPStatusCode_ProxyAuthenticationRequired = 407, 83 | kGCDWebServerHTTPStatusCode_RequestTimeout = 408, 84 | kGCDWebServerHTTPStatusCode_Conflict = 409, 85 | kGCDWebServerHTTPStatusCode_Gone = 410, 86 | kGCDWebServerHTTPStatusCode_LengthRequired = 411, 87 | kGCDWebServerHTTPStatusCode_PreconditionFailed = 412, 88 | kGCDWebServerHTTPStatusCode_RequestEntityTooLarge = 413, 89 | kGCDWebServerHTTPStatusCode_RequestURITooLong = 414, 90 | kGCDWebServerHTTPStatusCode_UnsupportedMediaType = 415, 91 | kGCDWebServerHTTPStatusCode_RequestedRangeNotSatisfiable = 416, 92 | kGCDWebServerHTTPStatusCode_ExpectationFailed = 417, 93 | kGCDWebServerHTTPStatusCode_UnprocessableEntity = 422, 94 | kGCDWebServerHTTPStatusCode_Locked = 423, 95 | kGCDWebServerHTTPStatusCode_FailedDependency = 424, 96 | kGCDWebServerHTTPStatusCode_UpgradeRequired = 426, 97 | kGCDWebServerHTTPStatusCode_PreconditionRequired = 428, 98 | kGCDWebServerHTTPStatusCode_TooManyRequests = 429, 99 | kGCDWebServerHTTPStatusCode_RequestHeaderFieldsTooLarge = 431 100 | }; 101 | 102 | /** 103 | * Convenience constants for "server error" HTTP status codes. 104 | */ 105 | typedef NS_ENUM(NSInteger, GCDWebServerServerErrorHTTPStatusCode) { 106 | kGCDWebServerHTTPStatusCode_InternalServerError = 500, 107 | kGCDWebServerHTTPStatusCode_NotImplemented = 501, 108 | kGCDWebServerHTTPStatusCode_BadGateway = 502, 109 | kGCDWebServerHTTPStatusCode_ServiceUnavailable = 503, 110 | kGCDWebServerHTTPStatusCode_GatewayTimeout = 504, 111 | kGCDWebServerHTTPStatusCode_HTTPVersionNotSupported = 505, 112 | kGCDWebServerHTTPStatusCode_InsufficientStorage = 507, 113 | kGCDWebServerHTTPStatusCode_LoopDetected = 508, 114 | kGCDWebServerHTTPStatusCode_NotExtended = 510, 115 | kGCDWebServerHTTPStatusCode_NetworkAuthenticationRequired = 511 116 | }; 117 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerPrivate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | #import 30 | 31 | /** 32 | * All GCDWebServer headers. 33 | */ 34 | 35 | #import "GCDWebServerHTTPStatusCodes.h" 36 | #import "GCDWebServerFunctions.h" 37 | 38 | #import "GCDWebServer.h" 39 | #import "GCDWebServerConnection.h" 40 | 41 | #import "GCDWebServerDataRequest.h" 42 | #import "GCDWebServerFileRequest.h" 43 | #import "GCDWebServerMultiPartFormRequest.h" 44 | #import "GCDWebServerURLEncodedFormRequest.h" 45 | 46 | #import "GCDWebServerDataResponse.h" 47 | #import "GCDWebServerErrorResponse.h" 48 | #import "GCDWebServerFileResponse.h" 49 | #import "GCDWebServerStreamedResponse.h" 50 | 51 | NS_ASSUME_NONNULL_BEGIN 52 | 53 | /** 54 | * Check if a custom logging facility should be used instead. 55 | */ 56 | 57 | #if defined(__GCDWEBSERVER_LOGGING_HEADER__) 58 | 59 | #define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__ 60 | 61 | #import __GCDWEBSERVER_LOGGING_HEADER__ 62 | 63 | /** 64 | * Automatically detect if XLFacility is available and if so use it as a 65 | * logging facility. 66 | */ 67 | 68 | #elif defined(__has_include) && __has_include("XLFacilityMacros.h") 69 | 70 | #define __GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__ 71 | 72 | #undef XLOG_TAG 73 | #define XLOG_TAG @"gcdwebserver.internal" 74 | 75 | #import "XLFacilityMacros.h" 76 | 77 | #define GWS_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__) 78 | #define GWS_LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__) 79 | #define GWS_LOG_INFO(...) XLOG_INFO(__VA_ARGS__) 80 | #define GWS_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__) 81 | #define GWS_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__) 82 | 83 | #define GWS_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__) 84 | #define GWS_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE() 85 | 86 | /** 87 | * If all of the above fail, then use GCDWebServer built-in 88 | * logging facility. 89 | */ 90 | 91 | #else 92 | 93 | #define __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__ 94 | 95 | typedef NS_ENUM(int, GCDWebServerLoggingLevel) { 96 | kGCDWebServerLoggingLevel_Debug = 0, 97 | kGCDWebServerLoggingLevel_Verbose, 98 | kGCDWebServerLoggingLevel_Info, 99 | kGCDWebServerLoggingLevel_Warning, 100 | kGCDWebServerLoggingLevel_Error 101 | }; 102 | 103 | extern GCDWebServerLoggingLevel GCDWebServerLogLevel; 104 | extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3); 105 | 106 | #if DEBUG 107 | #define GWS_LOG_DEBUG(...) \ 108 | do { \ 109 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \ 110 | } while (0) 111 | #else 112 | #define GWS_LOG_DEBUG(...) 113 | #endif 114 | #define GWS_LOG_VERBOSE(...) \ 115 | do { \ 116 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \ 117 | } while (0) 118 | #define GWS_LOG_INFO(...) \ 119 | do { \ 120 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); \ 121 | } while (0) 122 | #define GWS_LOG_WARNING(...) \ 123 | do { \ 124 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); \ 125 | } while (0) 126 | #define GWS_LOG_ERROR(...) \ 127 | do { \ 128 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); \ 129 | } while (0) 130 | 131 | #endif 132 | 133 | /** 134 | * Consistency check macros used when building Debug only. 135 | */ 136 | 137 | #if !defined(GWS_DCHECK) || !defined(GWS_DNOT_REACHED) 138 | 139 | #if DEBUG 140 | 141 | #define GWS_DCHECK(__CONDITION__) \ 142 | do { \ 143 | if (!(__CONDITION__)) { \ 144 | abort(); \ 145 | } \ 146 | } while (0) 147 | #define GWS_DNOT_REACHED() abort() 148 | 149 | #else 150 | 151 | #define GWS_DCHECK(__CONDITION__) 152 | #define GWS_DNOT_REACHED() 153 | 154 | #endif 155 | 156 | #endif 157 | 158 | /** 159 | * GCDWebServer internal constants and APIs. 160 | */ 161 | 162 | #define kGCDWebServerDefaultMimeType @"application/octet-stream" 163 | #define kGCDWebServerErrorDomain @"GCDWebServerErrorDomain" 164 | 165 | static inline BOOL GCDWebServerIsValidByteRange(NSRange range) { 166 | return ((range.location != NSUIntegerMax) || (range.length > 0)); 167 | } 168 | 169 | static inline NSError* GCDWebServerMakePosixError(int code) { 170 | return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}]; 171 | } 172 | 173 | extern void GCDWebServerInitializeFunctions(); 174 | extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value); 175 | extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value); 176 | extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute); 177 | extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset); 178 | extern BOOL GCDWebServerIsTextContentType(NSString* type); 179 | extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType); 180 | extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2); 181 | extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService); 182 | 183 | @interface GCDWebServerConnection () 184 | - (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket; 185 | @end 186 | 187 | @interface GCDWebServer () 188 | @property(nonatomic, readonly) NSMutableArray* handlers; 189 | @property(nonatomic, readonly, nullable) NSString* serverName; 190 | @property(nonatomic, readonly, nullable) NSString* authenticationRealm; 191 | @property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationBasicAccounts; 192 | @property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationDigestAccounts; 193 | @property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET; 194 | @property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority; 195 | - (void)willStartConnection:(GCDWebServerConnection*)connection; 196 | - (void)didEndConnection:(GCDWebServerConnection*)connection; 197 | @end 198 | 199 | @interface GCDWebServerHandler : NSObject 200 | @property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock; 201 | @property(nonatomic, readonly) GCDWebServerAsyncProcessBlock asyncProcessBlock; 202 | @end 203 | 204 | @interface GCDWebServerRequest () 205 | @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; 206 | @property(nonatomic) NSData* localAddressData; 207 | @property(nonatomic) NSData* remoteAddressData; 208 | - (void)prepareForWriting; 209 | - (BOOL)performOpen:(NSError**)error; 210 | - (BOOL)performWriteData:(NSData*)data error:(NSError**)error; 211 | - (BOOL)performClose:(NSError**)error; 212 | - (void)setAttribute:(nullable id)attribute forKey:(NSString*)key; 213 | @end 214 | 215 | @interface GCDWebServerResponse () 216 | @property(nonatomic, readonly) NSDictionary* additionalHeaders; 217 | @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; 218 | - (void)prepareForReading; 219 | - (BOOL)performOpen:(NSError**)error; 220 | - (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; 221 | - (void)performClose; 222 | @end 223 | 224 | NS_ASSUME_NONNULL_END 225 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * Attribute key to retrieve an NSArray containing NSStrings from a GCDWebServerRequest 34 | * with the contents of any regular expression captures done on the request path. 35 | * 36 | * @warning This attribute will only be set on the request if adding a handler using 37 | * -addHandlerForMethod:pathRegex:requestClass:processBlock:. 38 | */ 39 | extern NSString* const GCDWebServerRequestAttribute_RegexCaptures; 40 | 41 | /** 42 | * This protocol is used by the GCDWebServerConnection to communicate with 43 | * the GCDWebServerRequest and write the received HTTP body data. 44 | * 45 | * Note that multiple GCDWebServerBodyWriter objects can be chained together 46 | * internally e.g. to automatically decode gzip encoded content before 47 | * passing it on to the GCDWebServerRequest. 48 | * 49 | * @warning These methods can be called on any GCD thread. 50 | */ 51 | @protocol GCDWebServerBodyWriter 52 | 53 | /** 54 | * This method is called before any body data is received. 55 | * 56 | * It should return YES on success or NO on failure and set the "error" argument 57 | * which is guaranteed to be non-NULL. 58 | */ 59 | - (BOOL)open:(NSError**)error; 60 | 61 | /** 62 | * This method is called whenever body data has been received. 63 | * 64 | * It should return YES on success or NO on failure and set the "error" argument 65 | * which is guaranteed to be non-NULL. 66 | */ 67 | - (BOOL)writeData:(NSData*)data error:(NSError**)error; 68 | 69 | /** 70 | * This method is called after all body data has been received. 71 | * 72 | * It should return YES on success or NO on failure and set the "error" argument 73 | * which is guaranteed to be non-NULL. 74 | */ 75 | - (BOOL)close:(NSError**)error; 76 | 77 | @end 78 | 79 | /** 80 | * The GCDWebServerRequest class is instantiated by the GCDWebServerConnection 81 | * after the HTTP headers have been received. Each instance wraps a single HTTP 82 | * request. If a body is present, the methods from the GCDWebServerBodyWriter 83 | * protocol will be called by the GCDWebServerConnection to receive it. 84 | * 85 | * The default implementation of the GCDWebServerBodyWriter protocol on the class 86 | * simply ignores the body data. 87 | * 88 | * @warning GCDWebServerRequest instances can be created and used on any GCD thread. 89 | */ 90 | @interface GCDWebServerRequest : NSObject 91 | 92 | /** 93 | * Returns the HTTP method for the request. 94 | */ 95 | @property(nonatomic, readonly) NSString* method; 96 | 97 | /** 98 | * Returns the URL for the request. 99 | */ 100 | @property(nonatomic, readonly) NSURL* URL; 101 | 102 | /** 103 | * Returns the HTTP headers for the request. 104 | */ 105 | @property(nonatomic, readonly) NSDictionary* headers; 106 | 107 | /** 108 | * Returns the path component of the URL for the request. 109 | */ 110 | @property(nonatomic, readonly) NSString* path; 111 | 112 | /** 113 | * Returns the parsed and unescaped query component of the URL for the request. 114 | * 115 | * @warning This property will be nil if there is no query in the URL. 116 | */ 117 | @property(nonatomic, readonly, nullable) NSDictionary* query; 118 | 119 | /** 120 | * Returns the content type for the body of the request parsed from the 121 | * "Content-Type" header. 122 | * 123 | * This property will be nil if the request has no body or set to 124 | * "application/octet-stream" if a body is present but there was no 125 | * "Content-Type" header. 126 | */ 127 | @property(nonatomic, readonly, nullable) NSString* contentType; 128 | 129 | /** 130 | * Returns the content length for the body of the request parsed from the 131 | * "Content-Length" header. 132 | * 133 | * This property will be set to "NSUIntegerMax" if the request has no body or 134 | * if there is a body but no "Content-Length" header, typically because 135 | * chunked transfer encoding is used. 136 | */ 137 | @property(nonatomic, readonly) NSUInteger contentLength; 138 | 139 | /** 140 | * Returns the parsed "If-Modified-Since" header or nil if absent or malformed. 141 | */ 142 | @property(nonatomic, readonly, nullable) NSDate* ifModifiedSince; 143 | 144 | /** 145 | * Returns the parsed "If-None-Match" header or nil if absent or malformed. 146 | */ 147 | @property(nonatomic, readonly, nullable) NSString* ifNoneMatch; 148 | 149 | /** 150 | * Returns the parsed "Range" header or (NSUIntegerMax, 0) if absent or malformed. 151 | * The range will be set to (offset, length) if expressed from the beginning 152 | * of the entity body, or (NSUIntegerMax, length) if expressed from its end. 153 | */ 154 | @property(nonatomic, readonly) NSRange byteRange; 155 | 156 | /** 157 | * Returns YES if the client supports gzip content encoding according to the 158 | * "Accept-Encoding" header. 159 | */ 160 | @property(nonatomic, readonly) BOOL acceptsGzipContentEncoding; 161 | 162 | /** 163 | * Returns the address of the local peer (i.e. server) for the request 164 | * as a raw "struct sockaddr". 165 | */ 166 | @property(nonatomic, readonly) NSData* localAddressData; 167 | 168 | /** 169 | * Returns the address of the local peer (i.e. server) for the request 170 | * as a string. 171 | */ 172 | @property(nonatomic, readonly) NSString* localAddressString; 173 | 174 | /** 175 | * Returns the address of the remote peer (i.e. client) for the request 176 | * as a raw "struct sockaddr". 177 | */ 178 | @property(nonatomic, readonly) NSData* remoteAddressData; 179 | 180 | /** 181 | * Returns the address of the remote peer (i.e. client) for the request 182 | * as a string. 183 | */ 184 | @property(nonatomic, readonly) NSString* remoteAddressString; 185 | 186 | /** 187 | * This method is the designated initializer for the class. 188 | */ 189 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(nullable NSDictionary*)query; 190 | 191 | /** 192 | * Convenience method that checks if the contentType property is defined. 193 | */ 194 | - (BOOL)hasBody; 195 | 196 | /** 197 | * Convenience method that checks if the byteRange property is defined. 198 | */ 199 | - (BOOL)hasByteRange; 200 | 201 | /** 202 | * Retrieves an attribute associated with this request using the given key. 203 | * 204 | * @return The attribute value for the key. 205 | */ 206 | - (nullable id)attributeForKey:(NSString*)key; 207 | 208 | @end 209 | 210 | NS_ASSUME_NONNULL_END 211 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerRequestAttribute_RegexCaptures"; 37 | 38 | #define kZlibErrorDomain @"ZlibErrorDomain" 39 | #define kGZipInitialBufferSize (256 * 1024) 40 | 41 | @interface GCDWebServerBodyDecoder : NSObject 42 | @end 43 | 44 | @interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder 45 | @end 46 | 47 | @implementation GCDWebServerBodyDecoder { 48 | GCDWebServerRequest* __unsafe_unretained _request; 49 | id __unsafe_unretained _writer; 50 | } 51 | 52 | - (instancetype)initWithRequest:(GCDWebServerRequest* _Nonnull)request writer:(id _Nonnull)writer { 53 | if ((self = [super init])) { 54 | _request = request; 55 | _writer = writer; 56 | } 57 | return self; 58 | } 59 | 60 | - (BOOL)open:(NSError**)error { 61 | return [_writer open:error]; 62 | } 63 | 64 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 65 | return [_writer writeData:data error:error]; 66 | } 67 | 68 | - (BOOL)close:(NSError**)error { 69 | return [_writer close:error]; 70 | } 71 | 72 | @end 73 | 74 | @implementation GCDWebServerGZipDecoder { 75 | z_stream _stream; 76 | BOOL _finished; 77 | } 78 | 79 | - (BOOL)open:(NSError**)error { 80 | int result = inflateInit2(&_stream, 15 + 16); 81 | if (result != Z_OK) { 82 | if (error) { 83 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 84 | } 85 | return NO; 86 | } 87 | if (![super open:error]) { 88 | inflateEnd(&_stream); 89 | return NO; 90 | } 91 | return YES; 92 | } 93 | 94 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 95 | GWS_DCHECK(!_finished); 96 | _stream.next_in = (Bytef*)data.bytes; 97 | _stream.avail_in = (uInt)data.length; 98 | NSMutableData* decodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; 99 | if (decodedData == nil) { 100 | GWS_DNOT_REACHED(); 101 | return NO; 102 | } 103 | NSUInteger length = 0; 104 | while (1) { 105 | NSUInteger maxLength = decodedData.length - length; 106 | _stream.next_out = (Bytef*)((char*)decodedData.mutableBytes + length); 107 | _stream.avail_out = (uInt)maxLength; 108 | int result = inflate(&_stream, Z_NO_FLUSH); 109 | if ((result != Z_OK) && (result != Z_STREAM_END)) { 110 | if (error) { 111 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 112 | } 113 | return NO; 114 | } 115 | length += maxLength - _stream.avail_out; 116 | if (_stream.avail_out > 0) { 117 | if (result == Z_STREAM_END) { 118 | _finished = YES; 119 | } 120 | break; 121 | } 122 | decodedData.length = 2 * decodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available 123 | } 124 | decodedData.length = length; 125 | BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet 126 | return success; 127 | } 128 | 129 | - (BOOL)close:(NSError**)error { 130 | GWS_DCHECK(_finished); 131 | inflateEnd(&_stream); 132 | return [super close:error]; 133 | } 134 | 135 | @end 136 | 137 | @implementation GCDWebServerRequest { 138 | BOOL _opened; 139 | NSMutableArray* _decoders; 140 | id __unsafe_unretained _writer; 141 | NSMutableDictionary* _attributes; 142 | } 143 | 144 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { 145 | if ((self = [super init])) { 146 | _method = [method copy]; 147 | _URL = url; 148 | _headers = headers; 149 | _path = [path copy]; 150 | _query = query; 151 | 152 | _contentType = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]); 153 | _usesChunkedTransferEncoding = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"]; 154 | NSString* lengthHeader = [_headers objectForKey:@"Content-Length"]; 155 | if (lengthHeader) { 156 | NSInteger length = [lengthHeader integerValue]; 157 | if (_usesChunkedTransferEncoding || (length < 0)) { 158 | GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _URL); 159 | GWS_DNOT_REACHED(); 160 | return nil; 161 | } 162 | _contentLength = length; 163 | if (_contentType == nil) { 164 | _contentType = kGCDWebServerDefaultMimeType; 165 | } 166 | } else if (_usesChunkedTransferEncoding) { 167 | if (_contentType == nil) { 168 | _contentType = kGCDWebServerDefaultMimeType; 169 | } 170 | _contentLength = NSUIntegerMax; 171 | } else { 172 | if (_contentType) { 173 | GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _URL); 174 | _contentType = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense 175 | } 176 | _contentLength = NSUIntegerMax; 177 | } 178 | 179 | NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"]; 180 | if (modifiedHeader) { 181 | _ifModifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy]; 182 | } 183 | _ifNoneMatch = [_headers objectForKey:@"If-None-Match"]; 184 | 185 | _byteRange = NSMakeRange(NSUIntegerMax, 0); 186 | NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]); 187 | if (rangeHeader) { 188 | if ([rangeHeader hasPrefix:@"bytes="]) { 189 | NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","]; 190 | if (components.count == 1) { 191 | components = [[components firstObject] componentsSeparatedByString:@"-"]; 192 | if (components.count == 2) { 193 | NSString* startString = [components objectAtIndex:0]; 194 | NSInteger startValue = [startString integerValue]; 195 | NSString* endString = [components objectAtIndex:1]; 196 | NSInteger endValue = [endString integerValue]; 197 | if (startString.length && (startValue >= 0) && endString.length && (endValue >= startValue)) { // The second 500 bytes: "500-999" 198 | _byteRange.location = startValue; 199 | _byteRange.length = endValue - startValue + 1; 200 | } else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-" 201 | _byteRange.location = startValue; 202 | _byteRange.length = NSUIntegerMax; 203 | } else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500" 204 | _byteRange.location = NSUIntegerMax; 205 | _byteRange.length = endValue; 206 | } 207 | } 208 | } 209 | } 210 | if ((_byteRange.location == NSUIntegerMax) && (_byteRange.length == 0)) { // Ignore "Range" header if syntactically invalid 211 | GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url); 212 | } 213 | } 214 | 215 | if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) { 216 | _acceptsGzipContentEncoding = YES; 217 | } 218 | 219 | _decoders = [[NSMutableArray alloc] init]; 220 | _attributes = [[NSMutableDictionary alloc] init]; 221 | } 222 | return self; 223 | } 224 | 225 | - (BOOL)hasBody { 226 | return _contentType ? YES : NO; 227 | } 228 | 229 | - (BOOL)hasByteRange { 230 | return GCDWebServerIsValidByteRange(_byteRange); 231 | } 232 | 233 | - (id)attributeForKey:(NSString*)key { 234 | return [_attributes objectForKey:key]; 235 | } 236 | 237 | - (BOOL)open:(NSError**)error { 238 | return YES; 239 | } 240 | 241 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 242 | return YES; 243 | } 244 | 245 | - (BOOL)close:(NSError**)error { 246 | return YES; 247 | } 248 | 249 | - (void)prepareForWriting { 250 | _writer = self; 251 | if ([GCDWebServerNormalizeHeaderValue([self.headers objectForKey:@"Content-Encoding"]) isEqualToString:@"gzip"]) { 252 | GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer]; 253 | [_decoders addObject:decoder]; 254 | _writer = decoder; 255 | } 256 | } 257 | 258 | - (BOOL)performOpen:(NSError**)error { 259 | GWS_DCHECK(_contentType); 260 | GWS_DCHECK(_writer); 261 | if (_opened) { 262 | GWS_DNOT_REACHED(); 263 | return NO; 264 | } 265 | _opened = YES; 266 | return [_writer open:error]; 267 | } 268 | 269 | - (BOOL)performWriteData:(NSData*)data error:(NSError**)error { 270 | GWS_DCHECK(_opened); 271 | return [_writer writeData:data error:error]; 272 | } 273 | 274 | - (BOOL)performClose:(NSError**)error { 275 | GWS_DCHECK(_opened); 276 | return [_writer close:error]; 277 | } 278 | 279 | - (void)setAttribute:(id)attribute forKey:(NSString*)key { 280 | [_attributes setValue:attribute forKey:key]; 281 | } 282 | 283 | - (NSString*)localAddressString { 284 | return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES); 285 | } 286 | 287 | - (NSString*)remoteAddressString { 288 | return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES); 289 | } 290 | 291 | - (NSString*)description { 292 | NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path]; 293 | for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 294 | [description appendFormat:@"\n %@ = %@", argument, [_query objectForKey:argument]]; 295 | } 296 | [description appendString:@"\n"]; 297 | for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 298 | [description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]]; 299 | } 300 | return description; 301 | } 302 | 303 | @end 304 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the 34 | * GCDWebServerBodyReader object when reading data from it asynchronously. 35 | */ 36 | typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* _Nullable error); 37 | 38 | /** 39 | * This protocol is used by the GCDWebServerConnection to communicate with 40 | * the GCDWebServerResponse and read the HTTP body data to send. 41 | * 42 | * Note that multiple GCDWebServerBodyReader objects can be chained together 43 | * internally e.g. to automatically apply gzip encoding to the content before 44 | * passing it on to the GCDWebServerResponse. 45 | * 46 | * @warning These methods can be called on any GCD thread. 47 | */ 48 | @protocol GCDWebServerBodyReader 49 | 50 | @required 51 | 52 | /** 53 | * This method is called before any body data is sent. 54 | * 55 | * It should return YES on success or NO on failure and set the "error" argument 56 | * which is guaranteed to be non-NULL. 57 | */ 58 | - (BOOL)open:(NSError**)error; 59 | 60 | /** 61 | * This method is called whenever body data is sent. 62 | * 63 | * It should return a non-empty NSData if there is body data available, 64 | * or an empty NSData there is no more body data, or nil on error and set 65 | * the "error" argument which is guaranteed to be non-NULL. 66 | */ 67 | - (nullable NSData*)readData:(NSError**)error; 68 | 69 | /** 70 | * This method is called after all body data has been sent. 71 | */ 72 | - (void)close; 73 | 74 | @optional 75 | 76 | /** 77 | * If this method is implemented, it will be preferred over -readData:. 78 | * 79 | * It must call the passed block when data is available, passing a non-empty 80 | * NSData if there is body data available, or an empty NSData there is no more 81 | * body data, or nil on error and pass an NSError along. 82 | */ 83 | - (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; 84 | 85 | @end 86 | 87 | /** 88 | * The GCDWebServerResponse class is used to wrap a single HTTP response. 89 | * It is instantiated by the handler of the GCDWebServer that handled the request. 90 | * If a body is present, the methods from the GCDWebServerBodyReader protocol 91 | * will be called by the GCDWebServerConnection to send it. 92 | * 93 | * The default implementation of the GCDWebServerBodyReader protocol 94 | * on the class simply returns an empty body. 95 | * 96 | * @warning GCDWebServerResponse instances can be created and used on any GCD thread. 97 | */ 98 | @interface GCDWebServerResponse : NSObject 99 | 100 | /** 101 | * Sets the content type for the body of the response. 102 | * 103 | * The default value is nil i.e. the response has no body. 104 | * 105 | * @warning This property must be set if a body is present. 106 | */ 107 | @property(nonatomic, copy, nullable) NSString* contentType; 108 | 109 | /** 110 | * Sets the content length for the body of the response. If a body is present 111 | * but this property is set to "NSUIntegerMax", this means the length of the body 112 | * cannot be known ahead of time. Chunked transfer encoding will be 113 | * automatically enabled by the GCDWebServerConnection to comply with HTTP/1.1 114 | * specifications. 115 | * 116 | * The default value is "NSUIntegerMax" i.e. the response has no body or its length 117 | * is undefined. 118 | */ 119 | @property(nonatomic) NSUInteger contentLength; 120 | 121 | /** 122 | * Sets the HTTP status code for the response. 123 | * 124 | * The default value is 200 i.e. "OK". 125 | */ 126 | @property(nonatomic) NSInteger statusCode; 127 | 128 | /** 129 | * Sets the caching hint for the response using the "Cache-Control" header. 130 | * This value is expressed in seconds. 131 | * 132 | * The default value is 0 i.e. "no-cache". 133 | */ 134 | @property(nonatomic) NSUInteger cacheControlMaxAge; 135 | 136 | /** 137 | * Sets the last modified date for the response using the "Last-Modified" header. 138 | * 139 | * The default value is nil. 140 | */ 141 | @property(nonatomic, nullable) NSDate* lastModifiedDate; 142 | 143 | /** 144 | * Sets the ETag for the response using the "ETag" header. 145 | * 146 | * The default value is nil. 147 | */ 148 | @property(nonatomic, copy, nullable) NSString* eTag; 149 | 150 | /** 151 | * Enables gzip encoding for the response body. 152 | * 153 | * The default value is NO. 154 | * 155 | * @warning Enabling gzip encoding will remove any "Content-Length" header 156 | * since the length of the body is not known anymore. The client will still 157 | * be able to determine the body length when connection is closed per 158 | * HTTP/1.1 specifications. 159 | */ 160 | @property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled; 161 | 162 | /** 163 | * Creates an empty response. 164 | */ 165 | + (instancetype)response; 166 | 167 | /** 168 | * This method is the designated initializer for the class. 169 | */ 170 | - (instancetype)init; 171 | 172 | /** 173 | * Sets an additional HTTP header on the response. 174 | * Pass a nil value to remove an additional header. 175 | * 176 | * @warning Do not attempt to override the primary headers used 177 | * by GCDWebServerResponse like "Content-Type", "ETag", etc... 178 | */ 179 | - (void)setValue:(nullable NSString*)value forAdditionalHeader:(NSString*)header; 180 | 181 | /** 182 | * Convenience method that checks if the contentType property is defined. 183 | */ 184 | - (BOOL)hasBody; 185 | 186 | @end 187 | 188 | @interface GCDWebServerResponse (Extensions) 189 | 190 | /** 191 | * Creates a empty response with a specific HTTP status code. 192 | */ 193 | + (instancetype)responseWithStatusCode:(NSInteger)statusCode; 194 | 195 | /** 196 | * Creates an HTTP redirect response to a new URL. 197 | */ 198 | + (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent; 199 | 200 | /** 201 | * Initializes an empty response with a specific HTTP status code. 202 | */ 203 | - (instancetype)initWithStatusCode:(NSInteger)statusCode; 204 | 205 | /** 206 | * Initializes an HTTP redirect response to a new URL. 207 | */ 208 | - (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent; 209 | 210 | @end 211 | 212 | NS_ASSUME_NONNULL_END 213 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Core/GCDWebServerResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | #define kZlibErrorDomain @"ZlibErrorDomain" 37 | #define kGZipInitialBufferSize (256 * 1024) 38 | 39 | @interface GCDWebServerBodyEncoder : NSObject 40 | @end 41 | 42 | @interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder 43 | @end 44 | 45 | @implementation GCDWebServerBodyEncoder { 46 | GCDWebServerResponse* __unsafe_unretained _response; 47 | id __unsafe_unretained _reader; 48 | } 49 | 50 | - (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id _Nonnull)reader { 51 | if ((self = [super init])) { 52 | _response = response; 53 | _reader = reader; 54 | } 55 | return self; 56 | } 57 | 58 | - (BOOL)open:(NSError**)error { 59 | return [_reader open:error]; 60 | } 61 | 62 | - (NSData*)readData:(NSError**)error { 63 | return [_reader readData:error]; 64 | } 65 | 66 | - (void)close { 67 | [_reader close]; 68 | } 69 | 70 | @end 71 | 72 | @implementation GCDWebServerGZipEncoder { 73 | z_stream _stream; 74 | BOOL _finished; 75 | } 76 | 77 | - (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id _Nonnull)reader { 78 | if ((self = [super initWithResponse:response reader:reader])) { 79 | response.contentLength = NSUIntegerMax; // Make sure "Content-Length" header is not set since we don't know it 80 | [response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"]; 81 | } 82 | return self; 83 | } 84 | 85 | - (BOOL)open:(NSError**)error { 86 | int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); 87 | if (result != Z_OK) { 88 | if (error) { 89 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 90 | } 91 | return NO; 92 | } 93 | if (![super open:error]) { 94 | deflateEnd(&_stream); 95 | return NO; 96 | } 97 | return YES; 98 | } 99 | 100 | - (NSData*)readData:(NSError**)error { 101 | NSMutableData* encodedData; 102 | if (_finished) { 103 | encodedData = [[NSMutableData alloc] init]; 104 | } else { 105 | encodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; 106 | if (encodedData == nil) { 107 | GWS_DNOT_REACHED(); 108 | return nil; 109 | } 110 | NSUInteger length = 0; 111 | do { 112 | NSData* data = [super readData:error]; 113 | if (data == nil) { 114 | return nil; 115 | } 116 | _stream.next_in = (Bytef*)data.bytes; 117 | _stream.avail_in = (uInt)data.length; 118 | while (1) { 119 | NSUInteger maxLength = encodedData.length - length; 120 | _stream.next_out = (Bytef*)((char*)encodedData.mutableBytes + length); 121 | _stream.avail_out = (uInt)maxLength; 122 | int result = deflate(&_stream, data.length ? Z_NO_FLUSH : Z_FINISH); 123 | if (result == Z_STREAM_END) { 124 | _finished = YES; 125 | } else if (result != Z_OK) { 126 | if (error) { 127 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 128 | } 129 | return nil; 130 | } 131 | length += maxLength - _stream.avail_out; 132 | if (_stream.avail_out > 0) { 133 | break; 134 | } 135 | encodedData.length = 2 * encodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available 136 | } 137 | GWS_DCHECK(_stream.avail_in == 0); 138 | } while (length == 0); // Make sure we don't return an empty NSData if not in finished state 139 | encodedData.length = length; 140 | } 141 | return encodedData; 142 | } 143 | 144 | - (void)close { 145 | deflateEnd(&_stream); 146 | [super close]; 147 | } 148 | 149 | @end 150 | 151 | @implementation GCDWebServerResponse { 152 | BOOL _opened; 153 | NSMutableArray* _encoders; 154 | id __unsafe_unretained _reader; 155 | } 156 | 157 | + (instancetype)response { 158 | return [[[self class] alloc] init]; 159 | } 160 | 161 | - (instancetype)init { 162 | if ((self = [super init])) { 163 | _contentType = nil; 164 | _contentLength = NSUIntegerMax; 165 | _statusCode = kGCDWebServerHTTPStatusCode_OK; 166 | _cacheControlMaxAge = 0; 167 | _additionalHeaders = [[NSMutableDictionary alloc] init]; 168 | _encoders = [[NSMutableArray alloc] init]; 169 | } 170 | return self; 171 | } 172 | 173 | - (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header { 174 | [_additionalHeaders setValue:value forKey:header]; 175 | } 176 | 177 | - (BOOL)hasBody { 178 | return _contentType ? YES : NO; 179 | } 180 | 181 | - (BOOL)usesChunkedTransferEncoding { 182 | return (_contentType != nil) && (_contentLength == NSUIntegerMax); 183 | } 184 | 185 | - (BOOL)open:(NSError**)error { 186 | return YES; 187 | } 188 | 189 | - (NSData*)readData:(NSError**)error { 190 | return [NSData data]; 191 | } 192 | 193 | - (void)close { 194 | ; 195 | } 196 | 197 | - (void)prepareForReading { 198 | _reader = self; 199 | if (_gzipContentEncodingEnabled) { 200 | GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader]; 201 | [_encoders addObject:encoder]; 202 | _reader = encoder; 203 | } 204 | } 205 | 206 | - (BOOL)performOpen:(NSError**)error { 207 | GWS_DCHECK(_contentType); 208 | GWS_DCHECK(_reader); 209 | if (_opened) { 210 | GWS_DNOT_REACHED(); 211 | return NO; 212 | } 213 | _opened = YES; 214 | return [_reader open:error]; 215 | } 216 | 217 | - (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { 218 | GWS_DCHECK(_opened); 219 | if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) { 220 | [_reader asyncReadDataWithCompletion:[block copy]]; 221 | } else { 222 | NSError* error = nil; 223 | NSData* data = [_reader readData:&error]; 224 | block(data, error); 225 | } 226 | } 227 | 228 | - (void)performClose { 229 | GWS_DCHECK(_opened); 230 | [_reader close]; 231 | } 232 | 233 | - (NSString*)description { 234 | NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_statusCode]; 235 | if (_contentType) { 236 | [description appendFormat:@"\nContent Type = %@", _contentType]; 237 | } 238 | if (_contentLength != NSUIntegerMax) { 239 | [description appendFormat:@"\nContent Length = %lu", (unsigned long)_contentLength]; 240 | } 241 | [description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_cacheControlMaxAge]; 242 | if (_lastModifiedDate) { 243 | [description appendFormat:@"\nLast Modified Date = %@", _lastModifiedDate]; 244 | } 245 | if (_eTag) { 246 | [description appendFormat:@"\nETag = %@", _eTag]; 247 | } 248 | if (_additionalHeaders.count) { 249 | [description appendString:@"\n"]; 250 | for (NSString* header in [[_additionalHeaders allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 251 | [description appendFormat:@"\n%@: %@", header, [_additionalHeaders objectForKey:header]]; 252 | } 253 | } 254 | return description; 255 | } 256 | 257 | @end 258 | 259 | @implementation GCDWebServerResponse (Extensions) 260 | 261 | + (instancetype)responseWithStatusCode:(NSInteger)statusCode { 262 | return [[self alloc] initWithStatusCode:statusCode]; 263 | } 264 | 265 | + (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent { 266 | return [[self alloc] initWithRedirect:location permanent:permanent]; 267 | } 268 | 269 | - (instancetype)initWithStatusCode:(NSInteger)statusCode { 270 | if ((self = [self init])) { 271 | self.statusCode = statusCode; 272 | } 273 | return self; 274 | } 275 | 276 | - (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent { 277 | if ((self = [self init])) { 278 | self.statusCode = permanent ? kGCDWebServerHTTPStatusCode_MovedPermanently : kGCDWebServerHTTPStatusCode_TemporaryRedirect; 279 | [self setValue:[location absoluteString] forAdditionalHeader:@"Location"]; 280 | } 281 | return self; 282 | } 283 | 284 | @end 285 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Requests/GCDWebServerDataRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerDataRequest subclass of GCDWebServerRequest stores the body 34 | * of the HTTP request in memory. 35 | */ 36 | @interface GCDWebServerDataRequest : GCDWebServerRequest 37 | 38 | /** 39 | * Returns the data for the request body. 40 | */ 41 | @property(nonatomic, readonly) NSData* data; 42 | 43 | @end 44 | 45 | @interface GCDWebServerDataRequest (Extensions) 46 | 47 | /** 48 | * Returns the data for the request body interpreted as text. If the content 49 | * type of the body is not a text one, or if an error occurs, nil is returned. 50 | * 51 | * The text encoding used to interpret the data is extracted from the 52 | * "Content-Type" header or defaults to UTF-8. 53 | */ 54 | @property(nonatomic, readonly, nullable) NSString* text; 55 | 56 | /** 57 | * Returns the data for the request body interpreted as a JSON object. If the 58 | * content type of the body is not JSON, or if an error occurs, nil is returned. 59 | */ 60 | @property(nonatomic, readonly, nullable) id jsonObject; 61 | 62 | @end 63 | 64 | NS_ASSUME_NONNULL_END 65 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Requests/GCDWebServerDataRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @interface GCDWebServerDataRequest () 35 | @property(nonatomic) NSMutableData* data; 36 | @end 37 | 38 | @implementation GCDWebServerDataRequest { 39 | NSString* _text; 40 | id _jsonObject; 41 | } 42 | 43 | - (BOOL)open:(NSError**)error { 44 | if (self.contentLength != NSUIntegerMax) { 45 | _data = [[NSMutableData alloc] initWithCapacity:self.contentLength]; 46 | } else { 47 | _data = [[NSMutableData alloc] init]; 48 | } 49 | if (_data == nil) { 50 | if (error) { 51 | *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed allocating memory" }]; 52 | } 53 | return NO; 54 | } 55 | return YES; 56 | } 57 | 58 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 59 | [_data appendData:data]; 60 | return YES; 61 | } 62 | 63 | - (BOOL)close:(NSError**)error { 64 | return YES; 65 | } 66 | 67 | - (NSString*)description { 68 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 69 | if (_data) { 70 | [description appendString:@"\n\n"]; 71 | [description appendString:GCDWebServerDescribeData(_data, (NSString*)self.contentType)]; 72 | } 73 | return description; 74 | } 75 | 76 | @end 77 | 78 | @implementation GCDWebServerDataRequest (Extensions) 79 | 80 | - (NSString*)text { 81 | if (_text == nil) { 82 | if ([self.contentType hasPrefix:@"text/"]) { 83 | NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); 84 | _text = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 85 | } else { 86 | GWS_DNOT_REACHED(); 87 | } 88 | } 89 | return _text; 90 | } 91 | 92 | - (id)jsonObject { 93 | if (_jsonObject == nil) { 94 | NSString* mimeType = GCDWebServerTruncateHeaderValue(self.contentType); 95 | if ([mimeType isEqualToString:@"application/json"] || [mimeType isEqualToString:@"text/json"] || [mimeType isEqualToString:@"text/javascript"]) { 96 | _jsonObject = [NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL]; 97 | } else { 98 | GWS_DNOT_REACHED(); 99 | } 100 | } 101 | return _jsonObject; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Requests/GCDWebServerFileRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerFileRequest subclass of GCDWebServerRequest stores the body 34 | * of the HTTP request to a file on disk. 35 | */ 36 | @interface GCDWebServerFileRequest : GCDWebServerRequest 37 | 38 | /** 39 | * Returns the path to the temporary file containing the request body. 40 | * 41 | * @warning This temporary file will be automatically deleted when the 42 | * GCDWebServerFileRequest is deallocated. If you want to preserve this file, 43 | * you must move it to a different location beforehand. 44 | */ 45 | @property(nonatomic, readonly) NSString* temporaryPath; 46 | 47 | @end 48 | 49 | NS_ASSUME_NONNULL_END 50 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Requests/GCDWebServerFileRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerFileRequest { 35 | int _file; 36 | } 37 | 38 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { 39 | if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { 40 | _temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; 41 | } 42 | return self; 43 | } 44 | 45 | - (void)dealloc { 46 | unlink([_temporaryPath fileSystemRepresentation]); 47 | } 48 | 49 | - (BOOL)open:(NSError**)error { 50 | _file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 51 | if (_file <= 0) { 52 | if (error) { 53 | *error = GCDWebServerMakePosixError(errno); 54 | } 55 | return NO; 56 | } 57 | return YES; 58 | } 59 | 60 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 61 | if (write(_file, data.bytes, data.length) != (ssize_t)data.length) { 62 | if (error) { 63 | *error = GCDWebServerMakePosixError(errno); 64 | } 65 | return NO; 66 | } 67 | return YES; 68 | } 69 | 70 | - (BOOL)close:(NSError**)error { 71 | if (close(_file) < 0) { 72 | if (error) { 73 | *error = GCDWebServerMakePosixError(errno); 74 | } 75 | return NO; 76 | } 77 | #ifdef __GCDWEBSERVER_ENABLE_TESTING__ 78 | NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"]; 79 | if (creationDateHeader) { 80 | NSDate* date = GCDWebServerParseISO8601(creationDateHeader); 81 | if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate : date} ofItemAtPath:_temporaryPath error:error]) { 82 | return NO; 83 | } 84 | } 85 | NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"]; 86 | if (modifiedDateHeader) { 87 | NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader); 88 | if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate : date} ofItemAtPath:_temporaryPath error:error]) { 89 | return NO; 90 | } 91 | } 92 | #endif 93 | return YES; 94 | } 95 | 96 | - (NSString*)description { 97 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 98 | [description appendFormat:@"\n\n{%@}", _temporaryPath]; 99 | return description; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerMultiPart class is an abstract class that wraps the content 34 | * of a part. 35 | */ 36 | @interface GCDWebServerMultiPart : NSObject 37 | 38 | /** 39 | * Returns the control name retrieved from the part headers. 40 | */ 41 | @property(nonatomic, readonly) NSString* controlName; 42 | 43 | /** 44 | * Returns the content type retrieved from the part headers or "text/plain" 45 | * if not available (per HTTP specifications). 46 | */ 47 | @property(nonatomic, readonly) NSString* contentType; 48 | 49 | /** 50 | * Returns the MIME type component of the content type for the part. 51 | */ 52 | @property(nonatomic, readonly) NSString* mimeType; 53 | 54 | @end 55 | 56 | /** 57 | * The GCDWebServerMultiPartArgument subclass of GCDWebServerMultiPart wraps 58 | * the content of a part as data in memory. 59 | */ 60 | @interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart 61 | 62 | /** 63 | * Returns the data for the part. 64 | */ 65 | @property(nonatomic, readonly) NSData* data; 66 | 67 | /** 68 | * Returns the data for the part interpreted as text. If the content 69 | * type of the part is not a text one, or if an error occurs, nil is returned. 70 | * 71 | * The text encoding used to interpret the data is extracted from the 72 | * "Content-Type" header or defaults to UTF-8. 73 | */ 74 | @property(nonatomic, readonly, nullable) NSString* string; 75 | 76 | @end 77 | 78 | /** 79 | * The GCDWebServerMultiPartFile subclass of GCDWebServerMultiPart wraps 80 | * the content of a part as a file on disk. 81 | */ 82 | @interface GCDWebServerMultiPartFile : GCDWebServerMultiPart 83 | 84 | /** 85 | * Returns the file name retrieved from the part headers. 86 | */ 87 | @property(nonatomic, readonly) NSString* fileName; 88 | 89 | /** 90 | * Returns the path to the temporary file containing the part data. 91 | * 92 | * @warning This temporary file will be automatically deleted when the 93 | * GCDWebServerMultiPartFile is deallocated. If you want to preserve this file, 94 | * you must move it to a different location beforehand. 95 | */ 96 | @property(nonatomic, readonly) NSString* temporaryPath; 97 | 98 | @end 99 | 100 | /** 101 | * The GCDWebServerMultiPartFormRequest subclass of GCDWebServerRequest 102 | * parses the body of the HTTP request as a multipart encoded form. 103 | */ 104 | @interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest 105 | 106 | /** 107 | * Returns the argument parts from the multipart encoded form as 108 | * name / GCDWebServerMultiPartArgument pairs. 109 | */ 110 | @property(nonatomic, readonly) NSArray* arguments; 111 | 112 | /** 113 | * Returns the files parts from the multipart encoded form as 114 | * name / GCDWebServerMultiPartFile pairs. 115 | */ 116 | @property(nonatomic, readonly) NSArray* files; 117 | 118 | /** 119 | * Returns the MIME type for multipart encoded forms 120 | * i.e. "multipart/form-data". 121 | */ 122 | + (NSString*)mimeType; 123 | 124 | /** 125 | * Returns the first argument for a given control name or nil if not found. 126 | */ 127 | - (nullable GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name; 128 | 129 | /** 130 | * Returns the first file for a given control name or nil if not found. 131 | */ 132 | - (nullable GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name; 133 | 134 | @end 135 | 136 | NS_ASSUME_NONNULL_END 137 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerDataRequest.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerURLEncodedFormRequest subclass of GCDWebServerRequest 34 | * parses the body of the HTTP request as a URL encoded form using 35 | * GCDWebServerParseURLEncodedForm(). 36 | */ 37 | @interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest 38 | 39 | /** 40 | * Returns the unescaped control names and values for the URL encoded form. 41 | * 42 | * The text encoding used to interpret the data is extracted from the 43 | * "Content-Type" header or defaults to UTF-8. 44 | */ 45 | @property(nonatomic, readonly) NSDictionary* arguments; 46 | 47 | /** 48 | * Returns the MIME type for URL encoded forms 49 | * i.e. "application/x-www-form-urlencoded". 50 | */ 51 | + (NSString*)mimeType; 52 | 53 | @end 54 | 55 | NS_ASSUME_NONNULL_END 56 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerURLEncodedFormRequest 35 | 36 | + (NSString*)mimeType { 37 | return @"application/x-www-form-urlencoded"; 38 | } 39 | 40 | - (BOOL)close:(NSError**)error { 41 | if (![super close:error]) { 42 | return NO; 43 | } 44 | 45 | NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); 46 | NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 47 | _arguments = GCDWebServerParseURLEncodedForm(string); 48 | return YES; 49 | } 50 | 51 | - (NSString*)description { 52 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 53 | [description appendString:@"\n"]; 54 | for (NSString* argument in [[_arguments allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 55 | [description appendFormat:@"\n%@ = %@", argument, [_arguments objectForKey:argument]]; 56 | } 57 | return description; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Responses/GCDWebServerDataResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerDataResponse subclass of GCDWebServerResponse reads the body 34 | * of the HTTP response from memory. 35 | */ 36 | @interface GCDWebServerDataResponse : GCDWebServerResponse 37 | @property(nonatomic, copy) NSString* contentType; // Redeclare as non-null 38 | 39 | /** 40 | * Creates a response with data in memory and a given content type. 41 | */ 42 | + (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type; 43 | 44 | /** 45 | * This method is the designated initializer for the class. 46 | */ 47 | - (instancetype)initWithData:(NSData*)data contentType:(NSString*)type; 48 | 49 | @end 50 | 51 | @interface GCDWebServerDataResponse (Extensions) 52 | 53 | /** 54 | * Creates a data response from text encoded using UTF-8. 55 | */ 56 | + (nullable instancetype)responseWithText:(NSString*)text; 57 | 58 | /** 59 | * Creates a data response from HTML encoded using UTF-8. 60 | */ 61 | + (nullable instancetype)responseWithHTML:(NSString*)html; 62 | 63 | /** 64 | * Creates a data response from an HTML template encoded using UTF-8. 65 | * See -initWithHTMLTemplate:variables: for details. 66 | */ 67 | + (nullable instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; 68 | 69 | /** 70 | * Creates a data response from a serialized JSON object and the default 71 | * "application/json" content type. 72 | */ 73 | + (nullable instancetype)responseWithJSONObject:(id)object; 74 | 75 | /** 76 | * Creates a data response from a serialized JSON object and a custom 77 | * content type. 78 | */ 79 | + (nullable instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type; 80 | 81 | /** 82 | * Initializes a data response from text encoded using UTF-8. 83 | */ 84 | - (nullable instancetype)initWithText:(NSString*)text; 85 | 86 | /** 87 | * Initializes a data response from HTML encoded using UTF-8. 88 | */ 89 | - (nullable instancetype)initWithHTML:(NSString*)html; 90 | 91 | /** 92 | * Initializes a data response from an HTML template encoded using UTF-8. 93 | * 94 | * All occurences of "%variable%" within the HTML template are replaced with 95 | * their corresponding values. 96 | */ 97 | - (nullable instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; 98 | 99 | /** 100 | * Initializes a data response from a serialized JSON object and the default 101 | * "application/json" content type. 102 | */ 103 | - (nullable instancetype)initWithJSONObject:(id)object; 104 | 105 | /** 106 | * Initializes a data response from a serialized JSON object and a custom 107 | * content type. 108 | */ 109 | - (nullable instancetype)initWithJSONObject:(id)object contentType:(NSString*)type; 110 | 111 | @end 112 | 113 | NS_ASSUME_NONNULL_END 114 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Responses/GCDWebServerDataResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerDataResponse { 35 | NSData* _data; 36 | BOOL _done; 37 | } 38 | 39 | @dynamic contentType; 40 | 41 | + (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type { 42 | return [[[self class] alloc] initWithData:data contentType:type]; 43 | } 44 | 45 | - (instancetype)initWithData:(NSData*)data contentType:(NSString*)type { 46 | if ((self = [super init])) { 47 | _data = data; 48 | 49 | self.contentType = type; 50 | self.contentLength = data.length; 51 | } 52 | return self; 53 | } 54 | 55 | - (NSData*)readData:(NSError**)error { 56 | NSData* data; 57 | if (_done) { 58 | data = [NSData data]; 59 | } else { 60 | data = _data; 61 | _done = YES; 62 | } 63 | return data; 64 | } 65 | 66 | - (NSString*)description { 67 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 68 | [description appendString:@"\n\n"]; 69 | [description appendString:GCDWebServerDescribeData(_data, self.contentType)]; 70 | return description; 71 | } 72 | 73 | @end 74 | 75 | @implementation GCDWebServerDataResponse (Extensions) 76 | 77 | + (instancetype)responseWithText:(NSString*)text { 78 | return [[self alloc] initWithText:text]; 79 | } 80 | 81 | + (instancetype)responseWithHTML:(NSString*)html { 82 | return [[self alloc] initWithHTML:html]; 83 | } 84 | 85 | + (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { 86 | return [[self alloc] initWithHTMLTemplate:path variables:variables]; 87 | } 88 | 89 | + (instancetype)responseWithJSONObject:(id)object { 90 | return [[self alloc] initWithJSONObject:object]; 91 | } 92 | 93 | + (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type { 94 | return [[self alloc] initWithJSONObject:object contentType:type]; 95 | } 96 | 97 | - (instancetype)initWithText:(NSString*)text { 98 | NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding]; 99 | if (data == nil) { 100 | GWS_DNOT_REACHED(); 101 | return nil; 102 | } 103 | return [self initWithData:data contentType:@"text/plain; charset=utf-8"]; 104 | } 105 | 106 | - (instancetype)initWithHTML:(NSString*)html { 107 | NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding]; 108 | if (data == nil) { 109 | GWS_DNOT_REACHED(); 110 | return nil; 111 | } 112 | return [self initWithData:data contentType:@"text/html; charset=utf-8"]; 113 | } 114 | 115 | - (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { 116 | NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; 117 | [variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) { 118 | [html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)]; 119 | }]; 120 | return [self initWithHTML:html]; 121 | } 122 | 123 | - (instancetype)initWithJSONObject:(id)object { 124 | return [self initWithJSONObject:object contentType:@"application/json"]; 125 | } 126 | 127 | - (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type { 128 | NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL]; 129 | if (data == nil) { 130 | GWS_DNOT_REACHED(); 131 | return nil; 132 | } 133 | return [self initWithData:data contentType:type]; 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Responses/GCDWebServerErrorResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerDataResponse.h" 29 | #import "GCDWebServerHTTPStatusCodes.h" 30 | 31 | NS_ASSUME_NONNULL_BEGIN 32 | 33 | /** 34 | * The GCDWebServerDataResponse subclass of GCDWebServerDataResponse generates 35 | * an HTML body from an HTTP status code and an error message. 36 | */ 37 | @interface GCDWebServerErrorResponse : GCDWebServerDataResponse 38 | 39 | /** 40 | * Creates a client error response with the corresponding HTTP status code. 41 | */ 42 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 43 | 44 | /** 45 | * Creates a server error response with the corresponding HTTP status code. 46 | */ 47 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 48 | 49 | /** 50 | * Creates a client error response with the corresponding HTTP status code 51 | * and an underlying NSError. 52 | */ 53 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 54 | 55 | /** 56 | * Creates a server error response with the corresponding HTTP status code 57 | * and an underlying NSError. 58 | */ 59 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 60 | 61 | /** 62 | * Initializes a client error response with the corresponding HTTP status code. 63 | */ 64 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 65 | 66 | /** 67 | * Initializes a server error response with the corresponding HTTP status code. 68 | */ 69 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 70 | 71 | /** 72 | * Initializes a client error response with the corresponding HTTP status code 73 | * and an underlying NSError. 74 | */ 75 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 76 | 77 | /** 78 | * Initializes a server error response with the corresponding HTTP status code 79 | * and an underlying NSError. 80 | */ 81 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 82 | 83 | @end 84 | 85 | NS_ASSUME_NONNULL_END 86 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Responses/GCDWebServerErrorResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerErrorResponse 35 | 36 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 37 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 38 | va_list arguments; 39 | va_start(arguments, format); 40 | GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 41 | va_end(arguments); 42 | return response; 43 | } 44 | 45 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 46 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 47 | va_list arguments; 48 | va_start(arguments, format); 49 | GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 50 | va_end(arguments); 51 | return response; 52 | } 53 | 54 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 55 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 56 | va_list arguments; 57 | va_start(arguments, format); 58 | GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 59 | va_end(arguments); 60 | return response; 61 | } 62 | 63 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 64 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 65 | va_list arguments; 66 | va_start(arguments, format); 67 | GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 68 | va_end(arguments); 69 | return response; 70 | } 71 | 72 | static inline NSString* _EscapeHTMLString(NSString* string) { 73 | return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""]; 74 | } 75 | 76 | - (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments { 77 | NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; 78 | NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode]; 79 | NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @""; 80 | NSString* html = [NSString stringWithFormat:@"%@

%@: %@

%@

", 81 | title, title, _EscapeHTMLString(message), error]; 82 | if ((self = [self initWithHTML:html])) { 83 | self.statusCode = statusCode; 84 | } 85 | return self; 86 | } 87 | 88 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 89 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 90 | va_list arguments; 91 | va_start(arguments, format); 92 | self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 93 | va_end(arguments); 94 | return self; 95 | } 96 | 97 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 98 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 99 | va_list arguments; 100 | va_start(arguments, format); 101 | self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 102 | va_end(arguments); 103 | return self; 104 | } 105 | 106 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 107 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 108 | va_list arguments; 109 | va_start(arguments, format); 110 | self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 111 | va_end(arguments); 112 | return self; 113 | } 114 | 115 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 116 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 117 | va_list arguments; 118 | va_start(arguments, format); 119 | self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 120 | va_end(arguments); 121 | return self; 122 | } 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Responses/GCDWebServerFileResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerFileResponse subclass of GCDWebServerResponse reads the body 34 | * of the HTTP response from a file on disk. 35 | * 36 | * It will automatically set the contentType, lastModifiedDate and eTag 37 | * properties of the GCDWebServerResponse according to the file extension and 38 | * metadata. 39 | */ 40 | @interface GCDWebServerFileResponse : GCDWebServerResponse 41 | @property(nonatomic, copy) NSString* contentType; // Redeclare as non-null 42 | @property(nonatomic) NSDate* lastModifiedDate; // Redeclare as non-null 43 | @property(nonatomic, copy) NSString* eTag; // Redeclare as non-null 44 | 45 | /** 46 | * Creates a response with the contents of a file. 47 | */ 48 | + (nullable instancetype)responseWithFile:(NSString*)path; 49 | 50 | /** 51 | * Creates a response like +responseWithFile: and sets the "Content-Disposition" 52 | * HTTP header for a download if the "attachment" argument is YES. 53 | */ 54 | + (nullable instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment; 55 | 56 | /** 57 | * Creates a response like +responseWithFile: but restricts the file contents 58 | * to a specific byte range. 59 | * 60 | * See -initWithFile:byteRange: for details. 61 | */ 62 | + (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range; 63 | 64 | /** 65 | * Creates a response like +responseWithFile:byteRange: and sets the 66 | * "Content-Disposition" HTTP header for a download if the "attachment" 67 | * argument is YES. 68 | */ 69 | + (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; 70 | 71 | /** 72 | * Initializes a response with the contents of a file. 73 | */ 74 | - (nullable instancetype)initWithFile:(NSString*)path; 75 | 76 | /** 77 | * Initializes a response like +responseWithFile: and sets the 78 | * "Content-Disposition" HTTP header for a download if the "attachment" 79 | * argument is YES. 80 | */ 81 | - (nullable instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment; 82 | 83 | /** 84 | * Initializes a response like -initWithFile: but restricts the file contents 85 | * to a specific byte range. This range should be set to (NSUIntegerMax, 0) for 86 | * the full file, (offset, length) if expressed from the beginning of the file, 87 | * or (NSUIntegerMax, length) if expressed from the end of the file. The "offset" 88 | * and "length" values will be automatically adjusted to be compatible with the 89 | * actual size of the file. 90 | * 91 | * This argument would typically be set to the value of the byteRange property 92 | * of the current GCDWebServerRequest. 93 | */ 94 | - (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range; 95 | 96 | /** 97 | * This method is the designated initializer for the class. 98 | * 99 | * If MIME type overrides are specified, they allow to customize the built-in 100 | * mapping from extensions to MIME types. Keys of the dictionary must be lowercased 101 | * file extensions without the period, and the values must be the corresponding 102 | * MIME types. 103 | */ 104 | - (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(nullable NSDictionary*)overrides; 105 | 106 | @end 107 | 108 | NS_ASSUME_NONNULL_END 109 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Responses/GCDWebServerFileResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | #define kFileReadBufferSize (32 * 1024) 37 | 38 | @implementation GCDWebServerFileResponse { 39 | NSString* _path; 40 | NSUInteger _offset; 41 | NSUInteger _size; 42 | int _file; 43 | } 44 | 45 | @dynamic contentType, lastModifiedDate, eTag; 46 | 47 | + (instancetype)responseWithFile:(NSString*)path { 48 | return [[[self class] alloc] initWithFile:path]; 49 | } 50 | 51 | + (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment { 52 | return [[[self class] alloc] initWithFile:path isAttachment:attachment]; 53 | } 54 | 55 | + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range { 56 | return [[[self class] alloc] initWithFile:path byteRange:range]; 57 | } 58 | 59 | + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment { 60 | return [[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment mimeTypeOverrides:nil]; 61 | } 62 | 63 | - (instancetype)initWithFile:(NSString*)path { 64 | return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:NO mimeTypeOverrides:nil]; 65 | } 66 | 67 | - (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment { 68 | return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:attachment mimeTypeOverrides:nil]; 69 | } 70 | 71 | - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range { 72 | return [self initWithFile:path byteRange:range isAttachment:NO mimeTypeOverrides:nil]; 73 | } 74 | 75 | static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { 76 | return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)]; 77 | } 78 | 79 | - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(NSDictionary*)overrides { 80 | struct stat info; 81 | if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) { 82 | GWS_DNOT_REACHED(); 83 | return nil; 84 | } 85 | #ifndef __LP64__ 86 | if (info.st_size >= (off_t)4294967295) { // In 32 bit mode, we can't handle files greater than 4 GiBs (don't use "NSUIntegerMax" here to avoid potential unsigned to signed conversion issues) 87 | GWS_DNOT_REACHED(); 88 | return nil; 89 | } 90 | #endif 91 | NSUInteger fileSize = (NSUInteger)info.st_size; 92 | 93 | BOOL hasByteRange = GCDWebServerIsValidByteRange(range); 94 | if (hasByteRange) { 95 | if (range.location != NSUIntegerMax) { 96 | range.location = MIN(range.location, fileSize); 97 | range.length = MIN(range.length, fileSize - range.location); 98 | } else { 99 | range.length = MIN(range.length, fileSize); 100 | range.location = fileSize - range.length; 101 | } 102 | if (range.length == 0) { 103 | return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header 104 | } 105 | } else { 106 | range.location = 0; 107 | range.length = fileSize; 108 | } 109 | 110 | if ((self = [super init])) { 111 | _path = [path copy]; 112 | _offset = range.location; 113 | _size = range.length; 114 | if (hasByteRange) { 115 | [self setStatusCode:kGCDWebServerHTTPStatusCode_PartialContent]; 116 | [self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"]; 117 | GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path); 118 | } 119 | 120 | if (attachment) { 121 | NSString* fileName = [path lastPathComponent]; 122 | NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]; 123 | NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil; 124 | if (lossyFileName) { 125 | NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)]; 126 | [self setValue:value forAdditionalHeader:@"Content-Disposition"]; 127 | } else { 128 | GWS_DNOT_REACHED(); 129 | } 130 | } 131 | 132 | self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension], overrides); 133 | self.contentLength = _size; 134 | self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec); 135 | self.eTag = [NSString stringWithFormat:@"%llu/%li/%li", info.st_ino, info.st_mtimespec.tv_sec, info.st_mtimespec.tv_nsec]; 136 | } 137 | return self; 138 | } 139 | 140 | - (BOOL)open:(NSError**)error { 141 | _file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY); 142 | if (_file <= 0) { 143 | if (error) { 144 | *error = GCDWebServerMakePosixError(errno); 145 | } 146 | return NO; 147 | } 148 | if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) { 149 | if (error) { 150 | *error = GCDWebServerMakePosixError(errno); 151 | } 152 | close(_file); 153 | return NO; 154 | } 155 | return YES; 156 | } 157 | 158 | - (NSData*)readData:(NSError**)error { 159 | size_t length = MIN((NSUInteger)kFileReadBufferSize, _size); 160 | NSMutableData* data = [[NSMutableData alloc] initWithLength:length]; 161 | ssize_t result = read(_file, data.mutableBytes, length); 162 | if (result < 0) { 163 | if (error) { 164 | *error = GCDWebServerMakePosixError(errno); 165 | } 166 | return nil; 167 | } 168 | if (result > 0) { 169 | [data setLength:result]; 170 | _size -= result; 171 | } 172 | return data; 173 | } 174 | 175 | - (void)close { 176 | close(_file); 177 | } 178 | 179 | - (NSString*)description { 180 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 181 | [description appendFormat:@"\n\n{%@}", _path]; 182 | return description; 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Responses/GCDWebServerStreamedResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerStreamBlock is called to stream the data for the HTTP body. 34 | * The block must return either a chunk of data, an empty NSData when done, or 35 | * nil on error and set the "error" argument which is guaranteed to be non-NULL. 36 | */ 37 | typedef NSData* _Nullable (^GCDWebServerStreamBlock)(NSError** error); 38 | 39 | /** 40 | * The GCDWebServerAsyncStreamBlock works like the GCDWebServerStreamBlock 41 | * except the streamed data can be returned at a later time allowing for 42 | * truly asynchronous generation of the data. 43 | * 44 | * The block must call "completionBlock" passing the new chunk of data when ready, 45 | * an empty NSData when done, or nil on error and pass a NSError. 46 | * 47 | * The block cannot call "completionBlock" more than once per invocation. 48 | */ 49 | typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock); 50 | 51 | /** 52 | * The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams 53 | * the body of the HTTP response using a GCD block. 54 | */ 55 | @interface GCDWebServerStreamedResponse : GCDWebServerResponse 56 | @property(nonatomic, copy) NSString* contentType; // Redeclare as non-null 57 | 58 | /** 59 | * Creates a response with streamed data and a given content type. 60 | */ 61 | + (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; 62 | 63 | /** 64 | * Creates a response with async streamed data and a given content type. 65 | */ 66 | + (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; 67 | 68 | /** 69 | * Initializes a response with streamed data and a given content type. 70 | */ 71 | - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; 72 | 73 | /** 74 | * This method is the designated initializer for the class. 75 | */ 76 | - (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; 77 | 78 | @end 79 | 80 | NS_ASSUME_NONNULL_END 81 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebServer/Responses/GCDWebServerStreamedResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerStreamedResponse { 35 | GCDWebServerAsyncStreamBlock _block; 36 | } 37 | 38 | @dynamic contentType; 39 | 40 | + (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { 41 | return [[[self class] alloc] initWithContentType:type streamBlock:block]; 42 | } 43 | 44 | + (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { 45 | return [[[self class] alloc] initWithContentType:type asyncStreamBlock:block]; 46 | } 47 | 48 | - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { 49 | return [self initWithContentType:type 50 | asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { 51 | 52 | NSError* error = nil; 53 | NSData* data = block(&error); 54 | completionBlock(data, error); 55 | 56 | }]; 57 | } 58 | 59 | - (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { 60 | if ((self = [super init])) { 61 | _block = [block copy]; 62 | 63 | self.contentType = type; 64 | } 65 | return self; 66 | } 67 | 68 | - (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { 69 | _block(block); 70 | } 71 | 72 | - (NSString*)description { 73 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 74 | [description appendString:@"\n\n"]; 75 | return description; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjxjjx/GCDWebServerDemo/e6cee9613e6ebcf3866093959d2ead7ebbb1cbea/GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Info.plist -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | .row-file { 29 | height: 40px; 30 | } 31 | 32 | .column-icon { 33 | width: 40px; 34 | text-align: center; 35 | } 36 | 37 | .column-name { 38 | } 39 | 40 | .column-size { 41 | width: 100px; 42 | text-align: right; 43 | } 44 | 45 | .column-move { 46 | width: 40px; 47 | text-align: center; 48 | } 49 | 50 | .column-delete { 51 | width: 40px; 52 | text-align: center; 53 | } 54 | 55 | .column-path { 56 | } 57 | 58 | .column-progress { 59 | width: 200px; 60 | } 61 | 62 | .footer { 63 | color: #999; 64 | text-align: center; 65 | font-size: 0.9em; 66 | } 67 | 68 | #reload { 69 | float: right; 70 | } 71 | 72 | #create-input { 73 | width: 50%; 74 | height: 20px; 75 | } 76 | 77 | #move-input { 78 | width: 80%; 79 | height: 20px; 80 | } 81 | 82 | /* Bootstrap overrides */ 83 | 84 | .btn:focus { 85 | outline: none; /* FIXME: Work around for Chrome only but still draws focus ring while button pressed */ 86 | } 87 | 88 | .btn-toolbar { 89 | margin-top: 30px; 90 | margin-bottom: 20px; 91 | } 92 | 93 | .table .progress { 94 | margin-top: 0px; 95 | margin-bottom: 0px; 96 | height: 16px; 97 | } 98 | 99 | .panel-default > .panel-heading { 100 | color: #555; 101 | } 102 | 103 | .breadcrumb { 104 | background-color: transparent; 105 | border-radius: 0px; 106 | margin-bottom: 0px; 107 | padding: 0px; 108 | } 109 | 110 | .breadcrumb > .active { 111 | color: #555; 112 | } 113 | 114 | .breadcrumb > li + li:before { 115 | color: #999; 116 | } 117 | 118 | .table > tbody > tr > td { 119 | vertical-align: middle; 120 | } 121 | 122 | .table > tbody > tr > td > p { 123 | margin: 0px; 124 | } 125 | 126 | /* Initial state */ 127 | 128 | .uploading { 129 | display: none; 130 | } 131 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/jquery.fileupload.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /* 3 | * jQuery File Upload Plugin CSS 1.3.0 4 | * https://github.com/blueimp/jQuery-File-Upload 5 | * 6 | * Copyright 2013, Sebastian Tschan 7 | * https://blueimp.net 8 | * 9 | * Licensed under the MIT license: 10 | * http://www.opensource.org/licenses/MIT 11 | */ 12 | 13 | .fileinput-button { 14 | position: relative; 15 | overflow: hidden; 16 | } 17 | .fileinput-button input { 18 | position: absolute; 19 | top: 0; 20 | right: 0; 21 | margin: 0; 22 | opacity: 0; 23 | -ms-filter: 'alpha(opacity=0)'; 24 | font-size: 200px; 25 | direction: ltr; 26 | cursor: pointer; 27 | } 28 | 29 | /* Fixes for IE < 8 */ 30 | @media screen\9 { 31 | .fileinput-button input { 32 | filter: alpha(opacity=0); 33 | font-size: 100%; 34 | height: 100%; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROLOGUE" = "

Drag & drop files on this window or use the \"Upload Files…\" button to upload new files.

"; 2 | "EPILOGUE" = ""; 3 | "FOOTER_FORMAT" = "%@ %@"; 4 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjxjjx/GCDWebServerDemo/e6cee9613e6ebcf3866093959d2ead7ebbb1cbea/GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjxjjx/GCDWebServerDemo/e6cee9613e6ebcf3866093959d2ead7ebbb1cbea/GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjxjjx/GCDWebServerDemo/e6cee9613e6ebcf3866093959d2ead7ebbb1cbea/GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/index.html: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | %title% 35 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 57 |
58 | 59 | 62 | 63 | %prologue% 64 | 65 |
66 | 67 |
68 | 72 | 75 | 78 |
79 | 80 |
81 |
File Uploads in Progress
82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 |
90 |
91 | 92 | %epilogue% 93 | 94 | 95 | 96 |
97 | 98 | 118 | 119 | 139 | 140 | 171 | 172 | 187 | 188 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /GCDWebServerDemo/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); 8 | if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d= 1000000000) { 36 | return (bytes / 1000000000).toFixed(2) + ' GB'; 37 | } 38 | if (bytes >= 1000000) { 39 | return (bytes / 1000000).toFixed(2) + ' MB'; 40 | } 41 | return (bytes / 1000).toFixed(2) + ' KB'; 42 | } 43 | 44 | function _showError(message, textStatus, errorThrown) { 45 | $("#alerts").prepend(tmpl("template-alert", { 46 | level: "danger", 47 | title: (errorThrown != "" ? errorThrown : textStatus) + ": ", 48 | description: message 49 | })); 50 | } 51 | 52 | function _disableReloads() { 53 | _reloadingDisabled += 1; 54 | } 55 | 56 | function _enableReloads() { 57 | _reloadingDisabled -= 1; 58 | 59 | if (_pendingReloads.length > 0) { 60 | _reload(_pendingReloads.shift()); 61 | } 62 | } 63 | 64 | function _reload(path) { 65 | if (_reloadingDisabled) { 66 | if ($.inArray(path, _pendingReloads) < 0) { 67 | _pendingReloads.push(path); 68 | } 69 | return; 70 | } 71 | 72 | _disableReloads(); 73 | $.ajax({ 74 | url: 'list', 75 | type: 'GET', 76 | data: {path: path}, 77 | dataType: 'json' 78 | }).fail(function(jqXHR, textStatus, errorThrown) { 79 | _showError("Failed retrieving contents of \"" + path + "\"", textStatus, errorThrown); 80 | }).done(function(data, textStatus, jqXHR) { 81 | var scrollPosition = $(document).scrollTop(); 82 | 83 | if (path != _path) { 84 | $("#path").empty(); 85 | if (path == "/") { 86 | $("#path").append('
  • ' + _device + '
  • '); 87 | } else { 88 | $("#path").append('
  • ' + _device + '
  • '); 89 | var components = path.split("/").slice(1, -1); 90 | for (var i = 0; i < components.length - 1; ++i) { 91 | var subpath = "/" + components.slice(0, i + 1).join("/") + "/"; 92 | $("#path").append('
  • ' + components[i] + '
  • '); 93 | } 94 | $("#path > li").click(function(event) { 95 | _reload($(this).data("path")); 96 | event.preventDefault(); 97 | }); 98 | $("#path").append('
  • ' + components[components.length - 1] + '
  • '); 99 | } 100 | _path = path; 101 | } 102 | 103 | $("#listing").empty(); 104 | for (var i = 0, file; file = data[i]; ++i) { 105 | $(tmpl("template-listing", file)).data(file).appendTo("#listing"); 106 | } 107 | 108 | $(".edit").editable(function(value, settings) { 109 | var name = $(this).parent().parent().data("name"); 110 | if (value != name) { 111 | var path = $(this).parent().parent().data("path"); 112 | $.ajax({ 113 | url: 'move', 114 | type: 'POST', 115 | data: {oldPath: path, newPath: _path + value}, 116 | dataType: 'json' 117 | }).fail(function(jqXHR, textStatus, errorThrown) { 118 | _showError("Failed moving \"" + path + "\" to \"" + _path + value + "\"", textStatus, errorThrown); 119 | }).always(function() { 120 | _reload(_path); 121 | }); 122 | } 123 | return value; 124 | }, { 125 | onedit: function(settings, original) { 126 | _disableReloads(); 127 | }, 128 | onsubmit: function(settings, original) { 129 | _enableReloads(); 130 | }, 131 | onreset: function(settings, original) { 132 | _enableReloads(); 133 | }, 134 | tooltip: 'Click to rename...' 135 | }); 136 | 137 | $(".button-download").click(function(event) { 138 | var path = $(this).parent().parent().data("path"); 139 | setTimeout(function() { 140 | window.location = "download?path=" + encodeURIComponent(path); 141 | }, 0); 142 | }); 143 | 144 | $(".button-open").click(function(event) { 145 | var path = $(this).parent().parent().data("path"); 146 | _reload(path); 147 | }); 148 | 149 | $(".button-move").click(function(event) { 150 | var path = $(this).parent().parent().data("path"); 151 | if (path[path.length - 1] == "/") { 152 | path = path.slice(0, path.length - 1); 153 | } 154 | $("#move-input").data("path", path); 155 | $("#move-input").val(path); 156 | $("#move-modal").modal("show"); 157 | }); 158 | 159 | $(".button-delete").click(function(event) { 160 | var path = $(this).parent().parent().data("path"); 161 | $.ajax({ 162 | url: 'delete', 163 | type: 'POST', 164 | data: {path: path}, 165 | dataType: 'json' 166 | }).fail(function(jqXHR, textStatus, errorThrown) { 167 | _showError("Failed deleting \"" + path + "\"", textStatus, errorThrown); 168 | }).always(function() { 169 | _reload(_path); 170 | }); 171 | }); 172 | 173 | $(document).scrollTop(scrollPosition); 174 | }).always(function() { 175 | _enableReloads(); 176 | }); 177 | } 178 | 179 | $(document).ready(function() { 180 | 181 | // Workaround Firefox and IE not showing file selection dialog when clicking on "upload-file"