85 Commits
2.0.1 ... 2.3.1

Author SHA1 Message Date
Pierre-Olivier Latour
766072eb89 Renamed GCDWebServerStreamingResponse to GCDWebServerStreamedResponse.h 2014-04-19 17:43:36 -03:00
Pierre-Olivier Latour
0807cf5fe6 #33 Documented Requests/ and Responses/ 2014-04-19 17:39:09 -03:00
Pierre-Olivier Latour
c6701cd474 Renamed "showHiddenFiles" property to "allowHiddenItems" 2014-04-19 16:31:00 -03:00
Pierre-Olivier Latour
b6866bee8e #33 Documented GCDWebDAVServer/ and GCDWebUploader/ 2014-04-19 16:27:49 -03:00
Pierre-Olivier Latour
075774b6c0 Added -webServerDidCompleteBonjourRegistration: 2014-04-19 12:57:01 -03:00
Pierre-Olivier Latour
7743d18006 Added -logException: 2014-04-19 12:52:34 -03:00
Pierre-Olivier Latour
633d40f155 Don't cache GCDWebServerDataResponse 2014-04-19 12:51:36 -03:00
Pierre-Olivier Latour
5d82a80a34 #33 Documented Core/ 2014-04-19 12:51:00 -03:00
Pierre-Olivier Latour
5a26a09d8e Update README.md 2014-04-19 00:17:45 -03:00
Pierre-Olivier Latour
51e8dbe475 #41 Fixed linking issues with Podspec 2014-04-18 18:38:17 -03:00
Pierre-Olivier Latour
4a3d4537a3 Update README.md 2014-04-18 18:25:35 -03:00
Pierre-Olivier Latour
2597d6da00 Bumped version 2014-04-18 12:59:16 -03:00
Pierre-Olivier Latour
1ca5a56952 Allow multiple user accounts for authentication 2014-04-18 12:50:11 -03:00
Pierre-Olivier Latour
80cff01154 Update README.md 2014-04-18 12:33:59 -03:00
Pierre-Olivier Latour
1e17d5c455 #38 Added support for digest authentication 2014-04-18 12:32:55 -03:00
Pierre-Olivier Latour
ce1eb3c29a Fix 2014-04-18 12:31:41 -03:00
Pierre-Olivier Latour
f11647ee7f Fixed unit tests 2014-04-18 10:59:29 -03:00
Pierre-Olivier Latour
60f281fd30 #40 Allow non ISO Latin 1 file names when downloading files 2014-04-18 10:46:50 -03:00
Pierre-Olivier Latour
181984ba6d Update README.md 2014-04-17 23:10:10 -03:00
Pierre-Olivier Latour
4685fae29c Cleaned up authentication options 2014-04-17 23:08:16 -03:00
Pierre-Olivier Latour
8619799e62 Added -preflightRequest: and -overrideResponse:forRequest: APIs 2014-04-17 22:51:55 -03:00
Pierre-Olivier Latour
b1061378fb Fix 2014-04-17 22:16:25 -03:00
Pierre-Olivier Latour
9bc2bfa506 Update README.md 2014-04-17 18:06:15 -03:00
Pierre-Olivier Latour
078c5e7bc3 Bumped version 2014-04-17 11:50:17 -03:00
Pierre-Olivier Latour
3f304b3c14 Fixed parsing of 'multipart/form-data' with non-ASCII headers 2014-04-17 11:50:09 -03:00
Pierre-Olivier Latour
80348079a6 Added support for Basic Authentication 2014-04-17 11:14:17 -03:00
Pierre-Olivier Latour
099b2a03e6 Updated run APIs to use options 2014-04-17 11:12:18 -03:00
Pierre-Olivier Latour
f0c63f4776 Fixes 2014-04-17 10:41:33 -03:00
Pierre-Olivier Latour
4e3aa3bc5c Update README.md 2014-04-17 01:57:30 -03:00
Pierre-Olivier Latour
c5d82b85ed Update README.md 2014-04-17 01:55:15 -03:00
Pierre-Olivier Latour
b1181d2e40 Update README.md 2014-04-17 01:54:21 -03:00
Pierre-Olivier Latour
d931e04d47 Update README.md 2014-04-17 01:52:19 -03:00
Pierre-Olivier Latour
016c4da6d2 Update README.md 2014-04-17 01:50:07 -03:00
Pierre-Olivier Latour
38dd39a789 Update README.md 2014-04-17 01:49:03 -03:00
Pierre-Olivier Latour
748f6a8bc9 Update README.md 2014-04-17 01:48:22 -03:00
Pierre-Olivier Latour
0b3f825f1a Bumped version 2014-04-17 01:04:55 -03:00
Pierre-Olivier Latour
6b9142642e Update README.md 2014-04-17 00:47:34 -03:00
Pierre-Olivier Latour
0a21059d25 Added support for background mode on iOS 2014-04-16 23:49:35 -03:00
Pierre-Olivier Latour
14acb7b323 Fix 2014-04-16 23:30:22 -03:00
Pierre-Olivier Latour
2f7f7b7b50 Fix 2014-04-16 01:50:17 -03:00
Pierre-Olivier Latour
998a47b099 Replaced GCDWebServer subclassing with explicit options 2014-04-16 01:29:52 -03:00
Pierre-Olivier Latour
2d8996b91e Added connected state to GCDWebServer 2014-04-16 00:57:41 -03:00
Pierre-Olivier Latour
c5ca0f7cee Added GCDWebServerDelegate 2014-04-16 00:57:41 -03:00
Pierre-Olivier Latour
05a704bcef Fix 2014-04-16 00:57:41 -03:00
Pierre-Olivier Latour
c170fb4fd3 Use strerror() consistently 2014-04-16 00:57:41 -03:00
Pierre-Olivier Latour
55a9abd506 Fix 2014-04-16 00:57:39 -03:00
Pierre-Olivier Latour
489c8c6236 Added +[GCDWebServer maxPendingConnections] 2014-04-16 00:57:14 -03:00
Pierre-Olivier Latour
9c73736225 Fix 2014-04-15 21:53:02 -03:00
Pierre-Olivier Latour
1f503aa3b6 Fix 2014-04-15 18:02:38 -03:00
Pierre-Olivier Latour
4eeeab7dc2 Build iOS app using iOS Simulator SDK on Travis CI 2014-04-15 17:57:42 -03:00
Pierre-Olivier Latour
813f124f6a Fix 2014-04-15 17:57:15 -03:00
Pierre-Olivier Latour
8fe66444ae Cleaned up connections read / write implementation 2014-04-15 17:46:43 -03:00
Pierre-Olivier Latour
e3efc065df Build iOS app as part of unit tests 2014-04-15 16:50:57 -03:00
Pierre-Olivier Latour
016153f900 Isolated all testing code with __GCDWEBSERVER_ENABLE_TESTING__ 2014-04-15 16:50:57 -03:00
Pierre-Olivier Latour
a55781e2c1 Changed -[GCDWebServerConnection open] to return a BOOL 2014-04-15 16:50:57 -03:00
Pierre-Olivier Latour
4fb5d67e9b Fixed broken iOS build with __GCDWEBSERVER_ENABLE_TESTING__ 2014-04-15 16:50:57 -03:00
Pierre-Olivier Latour
14e04b445f Fixes 2014-04-15 16:50:57 -03:00
Pierre-Olivier Latour
ad01c15dcd Update README.md 2014-04-15 01:28:46 -03:00
Pierre-Olivier Latour
bb0f62416e Update README.md 2014-04-15 01:24:31 -03:00
Pierre-Olivier Latour
14228834d6 Update README.md 2014-04-15 01:09:10 -03:00
Pierre-Olivier Latour
eb16566605 Update README.md 2014-04-15 01:08:19 -03:00
Pierre-Olivier Latour
06e0fc531d Fixes 2014-04-15 01:01:55 -03:00
Pierre-Olivier Latour
ea777865f3 Bumped version 2014-04-15 00:33:17 -03:00
Pierre-Olivier Latour
5842149d4b Fixed unit tests to work in any time zone 2014-04-15 00:29:56 -03:00
Pierre-Olivier Latour
46ee1a48d6 Fixed source folder name typo! 2014-04-15 00:13:31 -03:00
Pierre-Olivier Latour
c1b2b79b06 Update README.md 2014-04-15 00:04:56 -03:00
Pierre-Olivier Latour
41c56334d0 Update README.md 2014-04-15 00:02:42 -03:00
Pierre-Olivier Latour
2810086816 Update README.md 2014-04-14 23:59:07 -03:00
Pierre-Olivier Latour
c8cd771697 Make header parsing more robust 2014-04-14 19:08:08 -03:00
Pierre-Olivier Latour
252c38c42a Force preserve scrolling position 2014-04-14 18:45:31 -03:00
Pierre-Olivier Latour
cedeb88cb6 Bumped version 2014-04-14 18:37:35 -03:00
Pierre-Olivier Latour
854bbdc6b2 Disable table reloads while renaming 2014-04-14 18:34:26 -03:00
Pierre-Olivier Latour
b949187770 Use jquery data() instead of element attributes 2014-04-14 18:28:22 -03:00
Pierre-Olivier Latour
dafcb0d895 Enable Enter key for dialogs 2014-04-14 10:34:43 -03:00
Pierre-Olivier Latour
7bceb8132a Force overwrite of entire GCDWebUploader.bundle on build 2014-04-14 10:34:14 -03:00
Pierre-Olivier Latour
4c2ac12f7b Fix 2014-04-13 19:20:55 -03:00
Pierre-Olivier Latour
1d2efbbbc7 Update README.md 2014-04-12 09:12:48 -07:00
Pierre-Olivier Latour
91b832715a Organized source code in subfolders 2014-04-12 09:11:24 -07:00
Pierre-Olivier Latour
0852bf2d05 Moved functions to GCDWebServerFunctions.[h/m] 2014-04-12 09:07:09 -07:00
Pierre-Olivier Latour
eb29232842 #34 Added Travis CI integration 2014-04-11 23:37:45 -07:00
Pierre-Olivier Latour
3b1fa05046 Update .travis.yml 2014-04-11 23:31:28 -07:00
Pierre-Olivier Latour
4535c5d61a Update Run-Tests.sh 2014-04-11 23:29:40 -07:00
Pierre-Olivier Latour
ccd1eaa880 Create .travis.yml 2014-04-11 23:27:35 -07:00
Pierre-Olivier Latour
1b805c3951 Update GCDWebServerRequest.h 2014-04-11 23:19:09 -07:00
Pierre-Olivier Latour
2172872787 Update README.md 2014-04-11 23:17:57 -07:00
121 changed files with 3379 additions and 1367 deletions

2
.travis.yml Normal file
View File

@@ -0,0 +1,2 @@
language: objective-c
script: ./Run-Tests.sh

View File

@@ -1,114 +0,0 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <TargetConditionals.h>
#import "GCDWebServerRequest.h"
#import "GCDWebServerResponse.h"
typedef NS_ENUM(int, GCDWebServerLogLevel) {
kGCDWebServerLogLevel_Debug = 0, // Only available if "NDEBUG" is not defined when building
kGCDWebServerLogLevel_Verbose,
kGCDWebServerLogLevel_Info,
kGCDWebServerLogLevel_Warning,
kGCDWebServerLogLevel_Error,
kGCDWebServerLogLevel_Exception,
};
typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request);
#ifdef __cplusplus
extern "C" {
#endif
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension);
NSString* GCDWebServerEscapeURLString(NSString* string);
NSString* GCDWebServerUnescapeURLString(NSString* string);
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
NSString* GCDWebServerGetPrimaryIPv4Address(); // Returns IPv4 address of primary connected service on OS X or of WiFi interface on iOS if connected
NSString* GCDWebServerFormatRFC822(NSDate* date);
NSDate* GCDWebServerParseRFC822(NSString* string);
NSString* GCDWebServerFormatISO8601(NSDate* date);
NSDate* GCDWebServerParseISO8601(NSString* string);
#ifdef __cplusplus
}
#endif
@interface GCDWebServer : NSObject
@property(nonatomic, readonly, getter=isRunning) BOOL running;
@property(nonatomic, readonly) NSUInteger port;
@property(nonatomic, readonly) NSString* bonjourName; // Only non-nil if Bonjour registration is active
- (instancetype)init;
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
- (void)removeAllHandlers;
- (BOOL)start; // Default is port 8080 (OS X & iOS Simulator) or 80 (iOS) and computer name
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name; // Pass nil name to disable Bonjour or empty string to use computer name
- (void)stop;
@end
@interface GCDWebServer (Subclassing)
+ (Class)connectionClass;
+ (NSString*)serverName; // Default is class name
+ (BOOL)shouldAutomaticallyMapHEADToGET; // Default is YES which means HEAD requests are mapped to GET requests with the response body being discarded
@end
@interface GCDWebServer (Extensions)
@property(nonatomic, readonly) NSURL* serverURL; // Only non-nil if server is running
@property(nonatomic, readonly) NSURL* bonjourServerURL; // Only non-nil if server is running and Bonjour registration is active
#if !TARGET_OS_IPHONE
@property(nonatomic, getter=isRecordingEnabled) BOOL recordingEnabled; // Creates files in the current directory containing the raw data for all requests and responses (directory most NOT contain prior recordings)
- (BOOL)runWithPort:(NSUInteger)port; // Starts then automatically stops on SIGINT i.e. Ctrl-C (use on main thread only)
#endif
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
- (NSInteger)runTestsInDirectory:(NSString*)path withPort:(NSUInteger)port; // Returns number of failed tests or -1 if server failed to start
#endif
@end
@interface GCDWebServer (Handlers)
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; // Path is case-insensitive
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; // Regular expression is case-insensitive
@end
@interface GCDWebServer (GETHandlers)
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge; // Path is case-insensitive
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests; // Path is case-insensitive
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests; // Base path is recursive and case-sensitive
@end
@interface GCDWebServer (Logging)
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
+ (void)setLogLevel:(GCDWebServerLogLevel)level; // Default level is DEBUG or INFO if "NDEBUG" is defined when building (it can also be set at runtime with the "logLevel" environment variable)
#endif
- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
@end

View File

@@ -1,50 +0,0 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "GCDWebServer.h"
@class GCDWebServerHandler;
@interface GCDWebServerConnection : NSObject
@property(nonatomic, readonly) GCDWebServer* server;
@property(nonatomic, readonly) NSData* localAddressData; // struct sockaddr
@property(nonatomic, readonly) NSString* localAddressString;
@property(nonatomic, readonly) NSData* remoteAddressData; // struct sockaddr
@property(nonatomic, readonly) NSString* remoteAddressString;
@property(nonatomic, readonly) NSUInteger totalBytesRead;
@property(nonatomic, readonly) NSUInteger totalBytesWritten;
@end
@interface GCDWebServerConnection (Subclassing)
- (void)open;
- (void)didUpdateBytesRead; // Called from arbitrary thread after @totalBytesRead is updated - Default implementation does nothing
- (void)didUpdateBytesWritten; // Called from arbitrary thread after @totalBytesWritten is updated - Default implementation does nothing
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block; // Only called if the request can be processed
- (GCDWebServerResponse*)replaceResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request; // Default implementation replaces any response matching the "ETag" or "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304) one
- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode; // If request headers was malformed, "request" will be nil
- (void)close;
@end

View File

@@ -1,51 +0,0 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/Foundation.h>
@protocol GCDWebServerBodyWriter <NSObject>
- (BOOL)open:(NSError**)error; // Return NO on error ("error" is guaranteed to be non-NULL)
- (BOOL)writeData:(NSData*)data error:(NSError**)error; // Return NO on error ("error" is guaranteed to be non-NULL)
- (BOOL)close:(NSError**)error; // Return NO on error ("error" is guaranteed to be non-NULL)
@end
@interface GCDWebServerRequest : NSObject <GCDWebServerBodyWriter>
@property(nonatomic, readonly) NSString* method;
@property(nonatomic, readonly) NSURL* URL;
@property(nonatomic, readonly) NSDictionary* headers;
@property(nonatomic, readonly) NSString* path;
@property(nonatomic, readonly) NSDictionary* query; // May be nil
@property(nonatomic, readonly) NSString* contentType; // Automatically parsed from headers (nil if request has no body or set to "application/octet-stream" if a body is present without a "Content-Type" header)
@property(nonatomic, readonly) NSUInteger contentLength; // Automatically parsed from headers (NSNotFound if request has no "Content-Length" header)
@property(nonatomic, readonly) NSDate* ifModifiedSince; // Automatically parsed from headers (nil if request has no "If-Modified-Since" header or it is malformatted)
@property(nonatomic, readonly) NSString* ifNoneMatch; // Automatically parsed from headers (nil if request has no "If-None-Match" header)
@property(nonatomic, readonly) NSRange byteRange; // Automatically parsed from headers ([NSNotFound, 0] if request has no "Range" header, [offset, length] for byte range from beginning or [NSNotFound, -bytes] from end)
@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding;
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query;
- (BOOL)hasBody; // Convenience method that checks if "contentType" is not nil
- (BOOL)hasByteRange; // Convenience method that checks "byteRange"
@end

View File

@@ -1,55 +0,0 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/Foundation.h>
@protocol GCDWebServerBodyReader <NSObject>
- (BOOL)open:(NSError**)error; // Return NO on error ("error" is guaranteed to be non-NULL)
- (NSData*)readData:(NSError**)error; // Must return nil on error or empty NSData if at end ("error" is guaranteed to be non-NULL)
- (void)close;
@end
@interface GCDWebServerResponse : NSObject <GCDWebServerBodyReader>
@property(nonatomic, copy) NSString* contentType; // Default is nil i.e. no body (must be set if a body is present)
@property(nonatomic) NSUInteger contentLength; // Default is NSNotFound i.e. undefined (if a body is present but length is undefined, chunked transfer encoding will be enabled)
@property(nonatomic) NSInteger statusCode; // Default is 200
@property(nonatomic) NSUInteger cacheControlMaxAge; // Default is 0 seconds i.e. "Cache-Control: no-cache"
@property(nonatomic, retain) NSDate* lastModifiedDate; // Default is nil i.e. no "Last-Modified" header
@property(nonatomic, copy) NSString* eTag; // Default is nil i.e. no "ETag" header
@property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled; // Default is disabled
+ (instancetype)response;
- (instancetype)init;
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header; // Pass nil value to remove header
- (BOOL)hasBody; // Convenience method that checks if "contentType" is not nil
@end
@interface GCDWebServerResponse (Extensions)
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode;
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
- (instancetype)initWithStatusCode:(NSInteger)statusCode;
- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
@end

View File

@@ -29,28 +29,129 @@
@class GCDWebDAVServer;
@protocol GCDWebDAVServerDelegate <NSObject>
/**
* Delegate methods for GCDWebDAVServer.
*
* @warning These methods are always called on the main thread in a serialized way.
*/
@protocol GCDWebDAVServerDelegate <GCDWebServerDelegate>
@optional
- (void)davServer:(GCDWebDAVServer*)uploader didDownloadFileAtPath:(NSString*)path;
- (void)davServer:(GCDWebDAVServer*)uploader didUploadFileAtPath:(NSString*)path;
- (void)davServer:(GCDWebDAVServer*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
- (void)davServer:(GCDWebDAVServer*)uploader didCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
- (void)davServer:(GCDWebDAVServer*)uploader didDeleteItemAtPath:(NSString*)path;
- (void)davServer:(GCDWebDAVServer*)uploader didCreateDirectoryAtPath:(NSString*)path;
/**
* This method is called whenever a file has been downloaded.
*/
- (void)davServer:(GCDWebDAVServer*)server didDownloadFileAtPath:(NSString*)path;
/**
* This method is called whenever a file has been uploaded.
*/
- (void)davServer:(GCDWebDAVServer*)server didUploadFileAtPath:(NSString*)path;
/**
* This method is called whenever a file or directory has been moved.
*/
- (void)davServer:(GCDWebDAVServer*)server didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
/**
* This method is called whenever a file or directory has been copied.
*/
- (void)davServer:(GCDWebDAVServer*)server didCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
/**
* This method is called whenever a file or directory has been deleted.
*/
- (void)davServer:(GCDWebDAVServer*)server didDeleteItemAtPath:(NSString*)path;
/**
* This method is called whenever a directory has been created.
*/
- (void)davServer:(GCDWebDAVServer*)server didCreateDirectoryAtPath:(NSString*)path;
@end
/**
* The GCDWebDAVServer subclass of GCDWebServer implements a class 1 compliant
* WebDAV server. It is also partially class 2 compliant (but only when the
* client is the OS X WebDAV implementation), so it can work with the OS X Finder.
*
* See the README.md file for more information about the features of GCDWebDAVServer.
*/
@interface GCDWebDAVServer : GCDWebServer
/**
* Returns the upload directory as specified when the server was initialized.
*/
@property(nonatomic, readonly) NSString* uploadDirectory;
/**
* Sets the delegate for the server.
*/
@property(nonatomic, assign) id<GCDWebDAVServerDelegate> delegate;
@property(nonatomic, copy) NSArray* allowedFileExtensions; // Default is nil i.e. all file extensions are allowed
@property(nonatomic) BOOL showHiddenFiles; // Default is NO
/**
* Restricts which files should be listed and allowed to be uploaded, downloaded,
* moved, copied or deleted depending on their extensions.
*
* The default value is nil i.e. all file extensions are allowed.
*/
@property(nonatomic, copy) NSArray* allowedFileExtensions;
/**
* Sets if files and directories whose name start with a period should be
* listed and allowed to be uploaded, downloaded, moved, copied or deleted.
*
* The default value is NO.
*/
@property(nonatomic) BOOL allowHiddenItems;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)initWithUploadDirectory:(NSString*)path;
@end
/**
* Hooks to customize the behavior of GCDWebDAVServer.
*
* @warning These methods can be called on any GCD thread.
*/
@interface GCDWebDAVServer (Subclassing)
- (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath; // Default implementation returns YES
- (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; // Default implementation returns YES
- (BOOL)shouldCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; // Default implementation returns YES
- (BOOL)shouldDeleteItemAtPath:(NSString*)path; // Default implementation returns YES
- (BOOL)shouldCreateDirectoryAtPath:(NSString*)path; // Default implementation returns YES
/**
* This method is called to check if a file is allowed to be uploaded.
* The uploaded file is available for inspection at "tempPath".
*
* The default implementation returns YES.
*/
- (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath;
/**
* This method is called to check if a file or directory is allowed to be moved.
*
* The default implementation returns YES.
*/
- (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
/**
* This method is called to check if a file or directory is allowed to be copied.
*
* The default implementation returns YES.
*/
- (BOOL)shouldCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
/**
* This method is called to check if a file or directory is allowed to be deleted.
*
* The default implementation returns YES.
*/
- (BOOL)shouldDeleteItemAtPath:(NSString*)path;
/**
* This method is called to check if a directory is allowed to be created.
*
* The default implementation returns YES.
*/
- (BOOL)shouldCreateDirectoryAtPath:(NSString*)path;
@end

View File

@@ -32,6 +32,8 @@
#import "GCDWebDAVServer.h"
#import "GCDWebServerFunctions.h"
#import "GCDWebServerDataRequest.h"
#import "GCDWebServerFileRequest.h"
@@ -52,9 +54,8 @@ typedef NS_ENUM(NSInteger, DAVProperties) {
@interface GCDWebDAVServer () {
@private
NSString* _uploadDirectory;
id<GCDWebDAVServerDelegate> __unsafe_unretained _delegate;
NSArray* _allowedExtensions;
BOOL _showHidden;
BOOL _allowHidden;
}
@end
@@ -91,7 +92,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
}
NSString* itemName = [absolutePath lastPathComponent];
if (([itemName hasPrefix:@"."] && !_showHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading item name \"%@\" is not allowed", itemName];
}
@@ -100,9 +101,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
return [GCDWebServerResponse response];
}
if ([_delegate respondsToSelector:@selector(davServer:didDownloadFileAtPath:)]) {
if ([self.delegate respondsToSelector:@selector(davServer:didDownloadFileAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didDownloadFileAtPath:absolutePath];
[self.delegate davServer:self didDownloadFileAtPath:absolutePath];
});
}
return [GCDWebServerFileResponse responseWithFile:absolutePath];
@@ -129,7 +130,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
}
NSString* fileName = [absolutePath lastPathComponent];
if (([fileName hasPrefix:@"."] && !_showHidden) || ![self _checkFileExtension:fileName]) {
if (([fileName hasPrefix:@"."] && !_allowHidden) || ![self _checkFileExtension:fileName]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file name \"%@\" is not allowed", fileName];
}
@@ -143,9 +144,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath];
}
if ([_delegate respondsToSelector:@selector(davServer:didUploadFileAtPath:)]) {
if ([self.delegate respondsToSelector:@selector(davServer:didUploadFileAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didUploadFileAtPath:absolutePath];
[self.delegate davServer:self didUploadFileAtPath:absolutePath];
});
}
return [GCDWebServerResponse responseWithStatusCode:(existing ? kGCDWebServerHTTPStatusCode_NoContent : kGCDWebServerHTTPStatusCode_Created)];
@@ -165,7 +166,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
}
NSString* itemName = [absolutePath lastPathComponent];
if (([itemName hasPrefix:@"."] && !_showHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName];
}
@@ -178,9 +179,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath];
}
if ([_delegate respondsToSelector:@selector(davServer:didDeleteItemAtPath:)]) {
if ([self.delegate respondsToSelector:@selector(davServer:didDeleteItemAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didDeleteItemAtPath:absolutePath];
[self.delegate davServer:self didDeleteItemAtPath:absolutePath];
});
}
return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NoContent];
@@ -202,7 +203,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
}
NSString* directoryName = [absolutePath lastPathComponent];
if (!_showHidden && [directoryName hasPrefix:@"."]) {
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName];
}
@@ -224,9 +225,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
}
#endif
if ([_delegate respondsToSelector:@selector(davServer:didCreateDirectoryAtPath:)]) {
if ([self.delegate respondsToSelector:@selector(davServer:didCreateDirectoryAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didCreateDirectoryAtPath:absolutePath];
[self.delegate davServer:self didCreateDirectoryAtPath:absolutePath];
});
}
return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Created];
@@ -263,7 +264,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
}
NSString* itemName = [dstAbsolutePath lastPathComponent];
if ((!_showHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"%@ to item name \"%@\" is not allowed", isMove ? @"Moving" : @"Copying", itemName];
}
@@ -296,15 +297,15 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
}
if (isMove) {
if ([_delegate respondsToSelector:@selector(davServer:didMoveItemFromPath:toPath:)]) {
if ([self.delegate respondsToSelector:@selector(davServer:didMoveItemFromPath:toPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didMoveItemFromPath:srcAbsolutePath toPath:dstAbsolutePath];
[self.delegate davServer:self didMoveItemFromPath:srcAbsolutePath toPath:dstAbsolutePath];
});
}
} else {
if ([_delegate respondsToSelector:@selector(davServer:didCopyItemFromPath:toPath:)]) {
if ([self.delegate respondsToSelector:@selector(davServer:didCopyItemFromPath:toPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didCopyItemFromPath:srcAbsolutePath toPath:dstAbsolutePath];
[self.delegate davServer:self didCopyItemFromPath:srcAbsolutePath toPath:dstAbsolutePath];
});
}
}
@@ -429,7 +430,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
}
NSString* itemName = [absolutePath lastPathComponent];
if (([itemName hasPrefix:@"."] && !_showHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Retrieving properties for item name \"%@\" is not allowed", itemName];
}
@@ -453,7 +454,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
relativePath = [relativePath stringByAppendingString:@"/"];
}
for (NSString* item in items) {
if (_showHidden || ![item hasPrefix:@"."]) {
if (_allowHidden || ![item hasPrefix:@"."]) {
[self _addPropertyResponseForItem:[absolutePath stringByAppendingPathComponent:item] resource:[relativePath stringByAppendingString:item] properties:properties xmlString:xmlString];
}
}
@@ -524,7 +525,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
}
NSString* itemName = [absolutePath lastPathComponent];
if ((!_showHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Locking item name \"%@\" is not allowed", itemName];
}
@@ -584,7 +585,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
}
NSString* itemName = [absolutePath lastPathComponent];
if ((!_showHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Unlocking item name \"%@\" is not allowed", itemName];
}
@@ -596,7 +597,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
@implementation GCDWebDAVServer
@synthesize uploadDirectory=_uploadDirectory, delegate=_delegate, allowedFileExtensions=_allowedExtensions, showHiddenFiles=_showHidden;
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden;
- (instancetype)initWithUploadDirectory:(NSString*)path {
if ((self = [super init])) {

View File

@@ -7,7 +7,7 @@
Pod::Spec.new do |s|
s.name = 'GCDWebServer'
s.version = '2.0.1'
s.version = '2.3'
s.author = { 'Pierre-Olivier Latour' => 'info@pol-online.net' }
s.license = { :type => 'BSD', :file => 'LICENSE' }
s.homepage = 'https://github.com/swisspol/GCDWebServer'
@@ -21,10 +21,12 @@ Pod::Spec.new do |s|
s.default_subspec = 'Core'
s.subspec 'Core' do |cs|
cs.source_files = 'CGDWebServer/*.{h,m}'
cs.source_files = 'GCDWebServer/**/*.{h,m}'
cs.requires_arc = true
cs.ios.library = 'z'
cs.ios.frameworks = 'MobileCoreServices', 'CFNetwork'
cs.osx.library = 'z'
cs.osx.framework = 'SystemConfiguration'
cs.compiler_flags = '-DNDEBUG' # TODO: Only set this for Release configuration
end

View File

@@ -24,36 +24,38 @@
/* Begin PBXBuildFile section */
E208D149167B76B700500836 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D148167B76B700500836 /* CFNetwork.framework */; };
E208D1B3167BB17E00500836 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D1B2167BB17E00500836 /* CoreServices.framework */; };
E22112851690B63A0048D2B2 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E221127D1690B63A0048D2B2 /* GCDWebServer.m */; };
E22112861690B63A0048D2B2 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E221127D1690B63A0048D2B2 /* GCDWebServer.m */; };
E22112871690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E221127F1690B63A0048D2B2 /* GCDWebServerConnection.m */; };
E22112881690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E221127F1690B63A0048D2B2 /* GCDWebServerConnection.m */; };
E22112891690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E22112821690B63A0048D2B2 /* GCDWebServerRequest.m */; };
E221128A1690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E22112821690B63A0048D2B2 /* GCDWebServerRequest.m */; };
E221128B1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E22112841690B63A0048D2B2 /* GCDWebServerResponse.m */; };
E221128C1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E22112841690B63A0048D2B2 /* GCDWebServerResponse.m */; };
E221128F1690B6470048D2B2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E221128E1690B6470048D2B2 /* main.m */; };
E22112951690B64F0048D2B2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E22112921690B64F0048D2B2 /* AppDelegate.m */; };
E22112971690B64F0048D2B2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E22112941690B64F0048D2B2 /* main.m */; };
E22112991690B7AA0048D2B2 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E22112981690B7AA0048D2B2 /* CFNetwork.framework */; };
E221129B1690B7B10048D2B2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129A1690B7B10048D2B2 /* UIKit.framework */; };
E221129D1690B7BA0048D2B2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */; };
E276647C18F3BC2100A034BA /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E276647B18F3BC2100A034BA /* GCDWebServerErrorResponse.m */; };
E276647D18F3BC2100A034BA /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E276647B18F3BC2100A034BA /* GCDWebServerErrorResponse.m */; };
E2A0E7ED18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */; };
E2A0E7EE18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */; };
E2A0E7F118F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */; };
E2A0E7F218F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */; };
E2A0E7F518F1D1E500C580B1 /* GCDWebServerStreamingResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamingResponse.m */; };
E2A0E7F618F1D1E500C580B1 /* GCDWebServerStreamingResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamingResponse.m */; };
E2A0E7F918F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */; };
E2A0E7FA18F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */; };
E2A0E7FD18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */; };
E2A0E7FE18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */; };
E2A0E80118F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80018F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m */; };
E2A0E80218F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80018F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m */; };
E2A0E80518F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */; };
E2A0E80618F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */; };
E28BAE3418F99C810095C089 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1718F99C810095C089 /* GCDWebServer.m */; };
E28BAE3518F99C810095C089 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1718F99C810095C089 /* GCDWebServer.m */; };
E28BAE3618F99C810095C089 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1918F99C810095C089 /* GCDWebServerConnection.m */; };
E28BAE3718F99C810095C089 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1918F99C810095C089 /* GCDWebServerConnection.m */; };
E28BAE3818F99C810095C089 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1B18F99C810095C089 /* GCDWebServerFunctions.m */; };
E28BAE3918F99C810095C089 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1B18F99C810095C089 /* GCDWebServerFunctions.m */; };
E28BAE3A18F99C810095C089 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1F18F99C810095C089 /* GCDWebServerRequest.m */; };
E28BAE3B18F99C810095C089 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1F18F99C810095C089 /* GCDWebServerRequest.m */; };
E28BAE3C18F99C810095C089 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2118F99C810095C089 /* GCDWebServerResponse.m */; };
E28BAE3D18F99C810095C089 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2118F99C810095C089 /* GCDWebServerResponse.m */; };
E28BAE3E18F99C810095C089 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2418F99C810095C089 /* GCDWebServerDataRequest.m */; };
E28BAE3F18F99C810095C089 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2418F99C810095C089 /* GCDWebServerDataRequest.m */; };
E28BAE4018F99C810095C089 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2618F99C810095C089 /* GCDWebServerFileRequest.m */; };
E28BAE4118F99C810095C089 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2618F99C810095C089 /* GCDWebServerFileRequest.m */; };
E28BAE4218F99C810095C089 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2818F99C810095C089 /* GCDWebServerMultiPartFormRequest.m */; };
E28BAE4318F99C810095C089 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2818F99C810095C089 /* GCDWebServerMultiPartFormRequest.m */; };
E28BAE4418F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2A18F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m */; };
E28BAE4518F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2A18F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m */; };
E28BAE4618F99C810095C089 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2D18F99C810095C089 /* GCDWebServerDataResponse.m */; };
E28BAE4718F99C810095C089 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2D18F99C810095C089 /* GCDWebServerDataResponse.m */; };
E28BAE4818F99C810095C089 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2F18F99C810095C089 /* GCDWebServerErrorResponse.m */; };
E28BAE4918F99C810095C089 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2F18F99C810095C089 /* GCDWebServerErrorResponse.m */; };
E28BAE4A18F99C810095C089 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE3118F99C810095C089 /* GCDWebServerFileResponse.m */; };
E28BAE4B18F99C810095C089 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE3118F99C810095C089 /* GCDWebServerFileResponse.m */; };
E28BAE4C18F99C810095C089 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE3318F99C810095C089 /* GCDWebServerStreamedResponse.m */; };
E28BAE4D18F99C810095C089 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE3318F99C810095C089 /* GCDWebServerStreamedResponse.m */; };
E2A0E80A18F3432600C580B1 /* GCDWebDAVServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80918F3432600C580B1 /* GCDWebDAVServer.m */; };
E2A0E80B18F3432600C580B1 /* GCDWebDAVServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80918F3432600C580B1 /* GCDWebDAVServer.m */; };
E2A0E80D18F35C9A00C580B1 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2A0E80C18F35C9A00C580B1 /* libxml2.dylib */; };
@@ -102,15 +104,6 @@
E208D148167B76B700500836 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
E208D1B2167BB17E00500836 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
E221125A1690B4DE0048D2B2 /* GCDWebServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GCDWebServer.app; sourceTree = BUILT_PRODUCTS_DIR; };
E221127C1690B63A0048D2B2 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
E221127D1690B63A0048D2B2 /* GCDWebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServer.m; sourceTree = "<group>"; };
E221127E1690B63A0048D2B2 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerConnection.h; sourceTree = "<group>"; };
E221127F1690B63A0048D2B2 /* GCDWebServerConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerConnection.m; sourceTree = "<group>"; };
E22112801690B63A0048D2B2 /* GCDWebServerPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerPrivate.h; sourceTree = "<group>"; };
E22112811690B63A0048D2B2 /* GCDWebServerRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerRequest.h; sourceTree = "<group>"; };
E22112821690B63A0048D2B2 /* GCDWebServerRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerRequest.m; sourceTree = "<group>"; };
E22112831690B63A0048D2B2 /* GCDWebServerResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerResponse.h; sourceTree = "<group>"; };
E22112841690B63A0048D2B2 /* GCDWebServerResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerResponse.m; sourceTree = "<group>"; };
E221128E1690B6470048D2B2 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
E22112911690B64F0048D2B2 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
E22112921690B64F0048D2B2 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -119,27 +112,38 @@
E22112981690B7AA0048D2B2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
E221129A1690B7B10048D2B2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; };
E276647A18F3BC2100A034BA /* GCDWebServerErrorResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerErrorResponse.h; sourceTree = "<group>"; };
E276647B18F3BC2100A034BA /* GCDWebServerErrorResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerErrorResponse.m; sourceTree = "<group>"; };
E2A0E7EB18F1D03700C580B1 /* GCDWebServerDataResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataResponse.h; sourceTree = "<group>"; };
E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataResponse.m; sourceTree = "<group>"; };
E2A0E7EF18F1D12E00C580B1 /* GCDWebServerFileResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileResponse.h; sourceTree = "<group>"; };
E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileResponse.m; sourceTree = "<group>"; };
E2A0E7F318F1D1E500C580B1 /* GCDWebServerStreamingResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerStreamingResponse.h; sourceTree = "<group>"; };
E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamingResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerStreamingResponse.m; sourceTree = "<group>"; };
E2A0E7F718F1D24700C580B1 /* GCDWebServerDataRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataRequest.h; sourceTree = "<group>"; };
E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataRequest.m; sourceTree = "<group>"; };
E2A0E7FB18F1D36C00C580B1 /* GCDWebServerFileRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileRequest.h; sourceTree = "<group>"; };
E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileRequest.m; sourceTree = "<group>"; };
E2A0E7FF18F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerMultiPartFormRequest.h; sourceTree = "<group>"; };
E2A0E80018F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerMultiPartFormRequest.m; sourceTree = "<group>"; };
E2A0E80318F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerURLEncodedFormRequest.h; sourceTree = "<group>"; };
E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerURLEncodedFormRequest.m; sourceTree = "<group>"; };
E28BAE1618F99C810095C089 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
E28BAE1718F99C810095C089 /* GCDWebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServer.m; sourceTree = "<group>"; };
E28BAE1818F99C810095C089 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerConnection.h; sourceTree = "<group>"; };
E28BAE1918F99C810095C089 /* GCDWebServerConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerConnection.m; sourceTree = "<group>"; };
E28BAE1A18F99C810095C089 /* GCDWebServerFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFunctions.h; sourceTree = "<group>"; };
E28BAE1B18F99C810095C089 /* GCDWebServerFunctions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFunctions.m; sourceTree = "<group>"; };
E28BAE1C18F99C810095C089 /* GCDWebServerHTTPStatusCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerHTTPStatusCodes.h; sourceTree = "<group>"; };
E28BAE1D18F99C810095C089 /* GCDWebServerPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerPrivate.h; sourceTree = "<group>"; };
E28BAE1E18F99C810095C089 /* GCDWebServerRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerRequest.h; sourceTree = "<group>"; };
E28BAE1F18F99C810095C089 /* GCDWebServerRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerRequest.m; sourceTree = "<group>"; };
E28BAE2018F99C810095C089 /* GCDWebServerResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerResponse.h; sourceTree = "<group>"; };
E28BAE2118F99C810095C089 /* GCDWebServerResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerResponse.m; sourceTree = "<group>"; };
E28BAE2318F99C810095C089 /* GCDWebServerDataRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataRequest.h; sourceTree = "<group>"; };
E28BAE2418F99C810095C089 /* GCDWebServerDataRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataRequest.m; sourceTree = "<group>"; };
E28BAE2518F99C810095C089 /* GCDWebServerFileRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileRequest.h; sourceTree = "<group>"; };
E28BAE2618F99C810095C089 /* GCDWebServerFileRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileRequest.m; sourceTree = "<group>"; };
E28BAE2718F99C810095C089 /* GCDWebServerMultiPartFormRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerMultiPartFormRequest.h; sourceTree = "<group>"; };
E28BAE2818F99C810095C089 /* GCDWebServerMultiPartFormRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerMultiPartFormRequest.m; sourceTree = "<group>"; };
E28BAE2918F99C810095C089 /* GCDWebServerURLEncodedFormRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerURLEncodedFormRequest.h; sourceTree = "<group>"; };
E28BAE2A18F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerURLEncodedFormRequest.m; sourceTree = "<group>"; };
E28BAE2C18F99C810095C089 /* GCDWebServerDataResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataResponse.h; sourceTree = "<group>"; };
E28BAE2D18F99C810095C089 /* GCDWebServerDataResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataResponse.m; sourceTree = "<group>"; };
E28BAE2E18F99C810095C089 /* GCDWebServerErrorResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerErrorResponse.h; sourceTree = "<group>"; };
E28BAE2F18F99C810095C089 /* GCDWebServerErrorResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerErrorResponse.m; sourceTree = "<group>"; };
E28BAE3018F99C810095C089 /* GCDWebServerFileResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileResponse.h; sourceTree = "<group>"; };
E28BAE3118F99C810095C089 /* GCDWebServerFileResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileResponse.m; sourceTree = "<group>"; };
E28BAE3218F99C810095C089 /* GCDWebServerStreamedResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerStreamedResponse.h; sourceTree = "<group>"; };
E28BAE3318F99C810095C089 /* GCDWebServerStreamedResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerStreamedResponse.m; sourceTree = "<group>"; };
E2A0E80818F3432600C580B1 /* GCDWebDAVServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebDAVServer.h; sourceTree = "<group>"; };
E2A0E80918F3432600C580B1 /* GCDWebDAVServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebDAVServer.m; sourceTree = "<group>"; };
E2A0E80C18F35C9A00C580B1 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/usr/lib/libxml2.dylib; sourceTree = DEVELOPER_DIR; };
E2A0E80E18F35CA300C580B1 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; };
E2A0E81018F3737B00C580B1 /* GCDWebServerHTTPStatusCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerHTTPStatusCodes.h; sourceTree = "<group>"; };
E2B0D4A618F13495009A7927 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
E2B0D4A818F134A8009A7927 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = GCDWebUploader.bundle; sourceTree = "<group>"; };
@@ -179,7 +183,7 @@
08FB7794FE84155DC02AAC07 /* LittleCMS */ = {
isa = PBXGroup;
children = (
E221127B1690B63A0048D2B2 /* CGDWebServer */,
E28BAE1418F99C810095C089 /* GCDWebServer */,
E2A0E80718F3432600C580B1 /* GCDWebDAVServer */,
E2BE850618E77ECA0061360B /* GCDWebUploader */,
E221128D1690B6470048D2B2 /* Mac */,
@@ -200,39 +204,6 @@
name = Products;
sourceTree = "<group>";
};
E221127B1690B63A0048D2B2 /* CGDWebServer */ = {
isa = PBXGroup;
children = (
E221127C1690B63A0048D2B2 /* GCDWebServer.h */,
E221127D1690B63A0048D2B2 /* GCDWebServer.m */,
E221127E1690B63A0048D2B2 /* GCDWebServerConnection.h */,
E221127F1690B63A0048D2B2 /* GCDWebServerConnection.m */,
E2A0E7F718F1D24700C580B1 /* GCDWebServerDataRequest.h */,
E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */,
E2A0E7EB18F1D03700C580B1 /* GCDWebServerDataResponse.h */,
E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */,
E276647A18F3BC2100A034BA /* GCDWebServerErrorResponse.h */,
E276647B18F3BC2100A034BA /* GCDWebServerErrorResponse.m */,
E2A0E7FB18F1D36C00C580B1 /* GCDWebServerFileRequest.h */,
E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */,
E2A0E7EF18F1D12E00C580B1 /* GCDWebServerFileResponse.h */,
E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */,
E2A0E81018F3737B00C580B1 /* GCDWebServerHTTPStatusCodes.h */,
E2A0E7FF18F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.h */,
E2A0E80018F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m */,
E22112801690B63A0048D2B2 /* GCDWebServerPrivate.h */,
E22112811690B63A0048D2B2 /* GCDWebServerRequest.h */,
E22112821690B63A0048D2B2 /* GCDWebServerRequest.m */,
E22112831690B63A0048D2B2 /* GCDWebServerResponse.h */,
E22112841690B63A0048D2B2 /* GCDWebServerResponse.m */,
E2A0E7F318F1D1E500C580B1 /* GCDWebServerStreamingResponse.h */,
E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamingResponse.m */,
E2A0E80318F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.h */,
E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */,
);
path = CGDWebServer;
sourceTree = "<group>";
};
E221128D1690B6470048D2B2 /* Mac */ = {
isa = PBXGroup;
children = (
@@ -276,6 +247,65 @@
name = "Mac Frameworks and Libraries";
sourceTree = "<group>";
};
E28BAE1418F99C810095C089 /* GCDWebServer */ = {
isa = PBXGroup;
children = (
E28BAE1518F99C810095C089 /* Core */,
E28BAE2218F99C810095C089 /* Requests */,
E28BAE2B18F99C810095C089 /* Responses */,
);
path = GCDWebServer;
sourceTree = "<group>";
};
E28BAE1518F99C810095C089 /* Core */ = {
isa = PBXGroup;
children = (
E28BAE1618F99C810095C089 /* GCDWebServer.h */,
E28BAE1718F99C810095C089 /* GCDWebServer.m */,
E28BAE1818F99C810095C089 /* GCDWebServerConnection.h */,
E28BAE1918F99C810095C089 /* GCDWebServerConnection.m */,
E28BAE1A18F99C810095C089 /* GCDWebServerFunctions.h */,
E28BAE1B18F99C810095C089 /* GCDWebServerFunctions.m */,
E28BAE1C18F99C810095C089 /* GCDWebServerHTTPStatusCodes.h */,
E28BAE1D18F99C810095C089 /* GCDWebServerPrivate.h */,
E28BAE1E18F99C810095C089 /* GCDWebServerRequest.h */,
E28BAE1F18F99C810095C089 /* GCDWebServerRequest.m */,
E28BAE2018F99C810095C089 /* GCDWebServerResponse.h */,
E28BAE2118F99C810095C089 /* GCDWebServerResponse.m */,
);
path = Core;
sourceTree = "<group>";
};
E28BAE2218F99C810095C089 /* Requests */ = {
isa = PBXGroup;
children = (
E28BAE2318F99C810095C089 /* GCDWebServerDataRequest.h */,
E28BAE2418F99C810095C089 /* GCDWebServerDataRequest.m */,
E28BAE2518F99C810095C089 /* GCDWebServerFileRequest.h */,
E28BAE2618F99C810095C089 /* GCDWebServerFileRequest.m */,
E28BAE2718F99C810095C089 /* GCDWebServerMultiPartFormRequest.h */,
E28BAE2818F99C810095C089 /* GCDWebServerMultiPartFormRequest.m */,
E28BAE2918F99C810095C089 /* GCDWebServerURLEncodedFormRequest.h */,
E28BAE2A18F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m */,
);
path = Requests;
sourceTree = "<group>";
};
E28BAE2B18F99C810095C089 /* Responses */ = {
isa = PBXGroup;
children = (
E28BAE2C18F99C810095C089 /* GCDWebServerDataResponse.h */,
E28BAE2D18F99C810095C089 /* GCDWebServerDataResponse.m */,
E28BAE2E18F99C810095C089 /* GCDWebServerErrorResponse.h */,
E28BAE2F18F99C810095C089 /* GCDWebServerErrorResponse.m */,
E28BAE3018F99C810095C089 /* GCDWebServerFileResponse.h */,
E28BAE3118F99C810095C089 /* GCDWebServerFileResponse.m */,
E28BAE3218F99C810095C089 /* GCDWebServerStreamedResponse.h */,
E28BAE3318F99C810095C089 /* GCDWebServerStreamedResponse.m */,
);
path = Responses;
sourceTree = "<group>";
};
E2A0E80718F3432600C580B1 /* GCDWebDAVServer */ = {
isa = PBXGroup;
children = (
@@ -302,6 +332,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "GCDWebServer (Mac)" */;
buildPhases = (
E21F038418FB37D80043AD1E /* Delete GCDWebUploader.bundle */,
E2BE850E18E788910061360B /* CopyFiles */,
8DD76FAB0486AB0100D96B5E /* Sources */,
8DD76FAD0486AB0100D96B5E /* Frameworks */,
@@ -374,26 +405,45 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
E21F038418FB37D80043AD1E /* Delete GCDWebUploader.bundle */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Delete GCDWebUploader.bundle";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "cd \"$BUILT_PRODUCTS_DIR\"\nrm -rf \"GCDWebUploader.bundle\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8DD76FAB0486AB0100D96B5E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E22112851690B63A0048D2B2 /* GCDWebServer.m in Sources */,
E2A0E7FD18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */,
E22112871690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,
E2A0E80518F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
E2A0E7F918F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */,
E22112891690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */,
E276647C18F3BC2100A034BA /* GCDWebServerErrorResponse.m in Sources */,
E2A0E7ED18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */,
E2A0E7F518F1D1E500C580B1 /* GCDWebServerStreamingResponse.m in Sources */,
E2A0E80118F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */,
E221128B1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */,
E28BAE4618F99C810095C089 /* GCDWebServerDataResponse.m in Sources */,
E28BAE3818F99C810095C089 /* GCDWebServerFunctions.m in Sources */,
E28BAE4A18F99C810095C089 /* GCDWebServerFileResponse.m in Sources */,
E28BAE4418F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
E28BAE3A18F99C810095C089 /* GCDWebServerRequest.m in Sources */,
E28BAE3418F99C810095C089 /* GCDWebServer.m in Sources */,
E28BAE3618F99C810095C089 /* GCDWebServerConnection.m in Sources */,
E28BAE3C18F99C810095C089 /* GCDWebServerResponse.m in Sources */,
E28BAE4018F99C810095C089 /* GCDWebServerFileRequest.m in Sources */,
E28BAE4C18F99C810095C089 /* GCDWebServerStreamedResponse.m in Sources */,
E28BAE3E18F99C810095C089 /* GCDWebServerDataRequest.m in Sources */,
E2A0E80A18F3432600C580B1 /* GCDWebDAVServer.m in Sources */,
E28BAE4218F99C810095C089 /* GCDWebServerMultiPartFormRequest.m in Sources */,
E2BE850C18E785940061360B /* GCDWebUploader.m in Sources */,
E221128F1690B6470048D2B2 /* main.m in Sources */,
E2A0E7F118F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */,
E28BAE4818F99C810095C089 /* GCDWebServerErrorResponse.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -402,21 +452,22 @@
buildActionMask = 2147483647;
files = (
E2A0E80B18F3432600C580B1 /* GCDWebDAVServer.m in Sources */,
E22112861690B63A0048D2B2 /* GCDWebServer.m in Sources */,
E276647D18F3BC2100A034BA /* GCDWebServerErrorResponse.m in Sources */,
E2A0E7EE18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */,
E2A0E80218F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */,
E22112881690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,
E221128A1690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */,
E221128C1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */,
E2A0E7FA18F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */,
E2A0E80618F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
E28BAE4918F99C810095C089 /* GCDWebServerErrorResponse.m in Sources */,
E28BAE4518F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
E28BAE4B18F99C810095C089 /* GCDWebServerFileResponse.m in Sources */,
E28BAE3918F99C810095C089 /* GCDWebServerFunctions.m in Sources */,
E28BAE4118F99C810095C089 /* GCDWebServerFileRequest.m in Sources */,
E28BAE4D18F99C810095C089 /* GCDWebServerStreamedResponse.m in Sources */,
E28BAE3F18F99C810095C089 /* GCDWebServerDataRequest.m in Sources */,
E2BE850B18E77ECA0061360B /* GCDWebUploader.m in Sources */,
E22112951690B64F0048D2B2 /* AppDelegate.m in Sources */,
E2A0E7FE18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */,
E2A0E7F618F1D1E500C580B1 /* GCDWebServerStreamingResponse.m in Sources */,
E2A0E7F218F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */,
E28BAE4718F99C810095C089 /* GCDWebServerDataResponse.m in Sources */,
E28BAE3D18F99C810095C089 /* GCDWebServerResponse.m in Sources */,
E28BAE3518F99C810095C089 /* GCDWebServer.m in Sources */,
E28BAE3718F99C810095C089 /* GCDWebServerConnection.m in Sources */,
E28BAE3B18F99C810095C089 /* GCDWebServerRequest.m in Sources */,
E22112971690B64F0048D2B2 /* main.m in Sources */,
E28BAE4318F99C810095C089 /* GCDWebServerMultiPartFormRequest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,475 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <TargetConditionals.h>
#import "GCDWebServerRequest.h"
#import "GCDWebServerResponse.h"
/**
* Log levels used by GCDWebServer.
*
* @warning kGCDWebServerLogLevel_Debug is only available if "NDEBUG" is not
* defined when building.
*/
typedef NS_ENUM(int, GCDWebServerLogLevel) {
kGCDWebServerLogLevel_Debug = 0,
kGCDWebServerLogLevel_Verbose,
kGCDWebServerLogLevel_Info,
kGCDWebServerLogLevel_Warning,
kGCDWebServerLogLevel_Error,
kGCDWebServerLogLevel_Exception,
};
/**
* The GCDWebServerMatchBlock is called for every handler added to the
* GCDWebServer whenever a new HTTP request has started (i.e. HTTP headers have
* been received). The block is passed the basic info for the request (HTTP method,
* URL, headers...) and must decide if it wants to handle it or not.
*
* If the handler can handle the request, the block must return a new
* GCDWebServerRequest instance created with the same basic info.
* Otherwise, it simply returns nil.
*/
typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
/**
* The GCDWebServerProcessBlock is called after the HTTP request has been fully
* received (i.e. the entire HTTP body has been read). THe block is passed the
* GCDWebServerRequest created at the previous step by the GCDWebServerMatchBlock.
*
* The block must return a GCDWebServerResponse or nil on error, which will
* result in a 500 HTTP status code returned to the client. It's however
* recommended to return a GCDWebServerErrorResponse on error so more useful
* information can be returned to the client.
*/
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request);
/**
* The port used by the GCDWebServer (NSNumber / NSUInteger).
*
* Default value is 0 i.e. let the OS pick a random port.
*/
extern NSString* const GCDWebServerOption_Port;
/**
* The Bonjour name used by the GCDWebServer (NSString).
*
* Default value is an empty string i.e. use the computer / device name.
*/
extern NSString* const GCDWebServerOption_BonjourName;
/**
* The maximum number of incoming HTTP requests that can be queued waiting to
* be handled before new ones are dropped (NSNumber / NSUInteger).
*
* Default value is 16.
*/
extern NSString* const GCDWebServerOption_MaxPendingConnections;
/**
* The value for "Server" HTTP header used by the GCDWebServer (NSString).
*
* Default value is the GCDWebServer class name.
*/
extern NSString* const GCDWebServerOption_ServerName;
/**
* The authentication method used by the GCDWebServer
* (one of "GCDWebServerAuthenticationMethod_...").
*
* Default value is nil i.e. authentication disabled.
*/
extern NSString* const GCDWebServerOption_AuthenticationMethod;
/**
* The authentication realm used by the GCDWebServer (NSString).
*
* Default value is the same as GCDWebServerOption_ServerName.
*/
extern NSString* const GCDWebServerOption_AuthenticationRealm;
/**
* The authentication accounts used by the GCDWebServer
* (NSDictionary of username / password pairs).
*
* Default value is nil i.e. no accounts.
*/
extern NSString* const GCDWebServerOption_AuthenticationAccounts;
/**
* The class used by the GCDWebServer when instantiating GCDWebServerConnection
* (subclass of GCDWebServerConnection).
*
* Default value is GCDWebServerConnection class.
*/
extern NSString* const GCDWebServerOption_ConnectionClass;
/**
* Allow the GCDWebServer to pretend "HEAD" requests are actually "GET" ones
* and automatically discard the HTTP body of the response (NSNumber / BOOL).
*
* Default value is YES.
*/
extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET;
/**
* The interval expressed in seconds used by the GCDWebServer to decide how to
* coalesce calls to -webServerDidConnect: and -webServerDidDisconnect:
* (NSNumber / double). Coalescing will be disabled if the interval is <= 0.0.
*
* Default value is 1.0 second.
*/
extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval;
#if TARGET_OS_IPHONE
/**
* Enables the GCDWebServer to automatically suspend itself (as if -stop was
* called) when the iOS app goes into the background and the last
* GCDWebServerConnection is closed, then resume itself (as if -start was called)
* when the iOS app comes back to the foreground (NSNumber / BOOL).
*
* See the README.md file for more information about this option.
*
* Default value is YES.
*
* @warning The running property will be NO while the GCDWebServer is suspended.
*/
extern NSString* const GCDWebServerOption_AutomaticallySuspendInBackground;
#endif
/**
* HTTP Basic Authentication scheme (see https://tools.ietf.org/html/rfc2617).
*
* @warning Use of this method is not recommended as the passwords are sent in clear.
*/
extern NSString* const GCDWebServerAuthenticationMethod_Basic;
/**
* HTTP Digest Access Authentication scheme (see https://tools.ietf.org/html/rfc2617).
*/
extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
@class GCDWebServer;
/**
* Delegate methods for GCDWebServer.
*
* @warning These methods are always called on the main thread in a serialized way.
*/
@protocol GCDWebServerDelegate <NSObject>
@optional
/**
* This method is called after the server has succesfully started.
*/
- (void)webServerDidStart:(GCDWebServer*)server;
/**
* This method is called after the Bonjour registration for the server has
* successfully completed.
*/
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server;
/**
* This method is called when the first GCDWebServerConnection is opened by the
* server to serve a series of HTTP requests. A series is ongoing as long as
* new HTTP requests keep coming (and new GCDWebServerConnection instances keep
* being opened), before the last HTTP request has been responded to (and the
* corresponding last GCDWebServerConnection closed).
*/
- (void)webServerDidConnect:(GCDWebServer*)server;
/**
* This method is called when the last GCDWebServerConnection is closed after
* the server has served a series of HTTP requests.
*
* The GCDWebServerOption_ConnectedStateCoalescingInterval option can be used
* to have the server wait some extra delay before considering that the series
* of HTTP requests has ended (in case there some latency between consecutive
* requests). This effectively coalesces the calls to -webServerDidConnect:
* and -webServerDidDisconnect:.
*/
- (void)webServerDidDisconnect:(GCDWebServer*)server;
/**
* This method is called after the server has stopped.
*/
- (void)webServerDidStop:(GCDWebServer*)server;
@end
/**
* The GCDWebServer class manages the socket that listens for HTTP requests and
* the list of handlers used to respond to them.
*
* See the README.md file for more information about the architecture of GCDWebServer.
*/
@interface GCDWebServer : NSObject
/**
* Sets the delegate for the server.
*/
@property(nonatomic, assign) id<GCDWebServerDelegate> delegate;
/**
* Indicates if the server is currently running.
*/
@property(nonatomic, readonly, getter=isRunning) BOOL running;
/**
* Returns the port used by the server.
*
* @warning This property is only valid if the server is running.
*/
@property(nonatomic, readonly) NSUInteger port;
/**
* Returns the Bonjour name in used by the server.
*
* @warning This property is only valid if the server is running and Bonjour
* registration has successfully completed, which can take up to a few seconds.
*/
@property(nonatomic, readonly) NSString* bonjourName;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)init;
/**
* Adds a handler to the server to handle incoming HTTP requests.
* Handlers are called in a LIFO queue, so the latest added handler overrides
* any previously added ones.
*
* @warning Addling handlers while the GCDWebServer is running is not allowed.
*/
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
/**
* Removes all handlers previously added to the server.
*
* @warning Removing handlers while the GCDWebServer is running is not allowed.
*/
- (void)removeAllHandlers;
/**
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
* using the computer / device name for as the Bonjour name.
*
* Returns NO if the server failed to start.
*/
- (BOOL)start;
/**
* Starts the server on a given port and with a specific Bonjour name.
* Pass a nil Bonjour name to disable Bonjour entirely or an empty string to
* use the computer / device name.
*
* Returns NO if the server failed to start.
*/
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name;
/**
* Starts the server with explicit options. This method is the designated way
* to start the server.
*
* Returns NO if the server failed to start.
*/
- (BOOL)startWithOptions:(NSDictionary*)options;
/**
* Stops the server and prevents it to accepts new HTTP requests.
*
* @warning Stopping the server does not abort GCDWebServerConnection instances
* handling already received HTTP requests. These connections will continue to
* execute until the corresponding requests and responses are completed.
*/
- (void)stop;
@end
@interface GCDWebServer (Extensions)
/**
* Returns the server's URL.
*
* @warning This property is only valid if the server is running.
*/
@property(nonatomic, readonly) NSURL* serverURL;
/**
* Returns the server's Bonjour URL.
*
* @warning This property is only valid if the server is running and Bonjour
* registration has successfully completed, which can take up to a few seconds.
*/
@property(nonatomic, readonly) NSURL* bonjourServerURL;
#if !TARGET_OS_IPHONE
/**
* Runs the server synchronously using -startWithPort:bonjourName: until a
* SIGINT signal is received i.e. Ctrl-C. This method is intended to be used
* by command line tools.
*
* Returns NO if the server failed to start.
*
* @warning This method must be used from the main thread only.
*/
- (BOOL)runWithPort:(NSUInteger)port bonjourName:(NSString*)name;
/**
* Runs the server synchronously using -startWithOptions: until a SIGINT signal
* is received i.e. Ctrl-C. This method is intended to be used by command line
* tools.
*
* Returns NO if the server failed to start.
*
* @warning This method must be used from the main thread only.
*/
- (BOOL)runWithOptions:(NSDictionary*)options;
#endif
@end
@interface GCDWebServer (Handlers)
/**
* Adds a default handler to the server to handle all incoming HTTP requests
* with a given HTTP method.
*/
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
/**
* Adds a handler to the server to handle incoming HTTP requests with a given
* HTTP method and a specific case-insensitive path.
*/
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
/**
* Adds a handler to the server to handle incoming HTTP requests with a given
* HTTP method and a path matching a case-insensitive regular expression.
*/
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
@end
@interface GCDWebServer (GETHandlers)
/**
* Adds a handler to the server to respond to incoming "GET" HTTP requests
* with a specific case-insensitive path with in-memory data.
*/
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge;
/**
* Adds a handler to the server to respond to incoming "GET" HTTP requests
* with a specific case-insensitive path with a file.
*/
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests;
/**
* Adds a handler to the server to respond to incoming "GET" HTTP requests
* with a case-insensitive path inside a base path with the corresponding file
* inside a local directory. If no local file matches the request path, a 401
* HTTP status code is returned to the client.
*
* The "indexFilename" argument allows to specify an "index" file name to use
* when the request path corresponds to a directory.
*/
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests;
@end
@interface GCDWebServer (Logging)
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
/**
* Sets the current log level below which logged messages are discarded.
*
* The default level is either DEBUG or INFO if "NDEBUG" is defined at build-time.
* It can also be set at runtime with the "logLevel" environment variable.
*/
+ (void)setLogLevel:(GCDWebServerLogLevel)level;
#endif
/**
* Logs a message with the kGCDWebServerLogLevel_Verbose level.
*/
- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
/**
* Logs a message with the kGCDWebServerLogLevel_Info level.
*/
- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
/**
* Logs a message with the kGCDWebServerLogLevel_Warning level.
*/
- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
/**
* Logs a message with the kGCDWebServerLogLevel_Error level.
*/
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
/**
* Logs an exception with the kGCDWebServerLogLevel_Exception level.
*/
- (void)logException:(NSException*)exception;
@end
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
@interface GCDWebServer (Testing)
/**
* Activates recording of HTTP requests and responses which create files in the
* current directory containing the raw data for all requests and responses.
*
* @warning The current directory must not contain any prior recording files.
*/
@property(nonatomic, getter=isRecordingEnabled) BOOL recordingEnabled;
/**
* Runs tests by playing back pre-recorded HTTP requests in the given directory
* and comparing the generated responses with the pre-recorded ones.
*
* Returns the number of failed tests or -1 if server failed to start.
*/
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path;
@end
#endif

View File

@@ -0,0 +1,160 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "GCDWebServer.h"
@class GCDWebServerHandler;
/**
* The GCDWebServerConnection class is instantiated by GCDWebServer to handle
* each new HTTP connection. Each instance stays alive until the connection is
* closed.
*
* You cannot use this class directly, but it is made public so you can
* subclass it to override some hooks. Use the GCDWebServerOption_ConnectionClass
* option for GCDWebServer to install your custom subclass.
*
* @warning The GCDWebServerConnection retains the GCDWebServer
* until the connection is closed.
*/
@interface GCDWebServerConnection : NSObject
/**
* Returns the GCDWebServer that owns the connection.
*/
@property(nonatomic, readonly) GCDWebServer* server;
/**
* Returns the address of the local peer (i.e. server) of the connection
* as a raw "struct sockaddr".
*/
@property(nonatomic, readonly) NSData* localAddressData;
/**
* Returns the address of the local peer (i.e. server) of the connection
* as a dotted string.
*/
@property(nonatomic, readonly) NSString* localAddressString;
/**
* Returns the address of the remote peer (i.e. client) of the connection
* as a raw "struct sockaddr".
*/
@property(nonatomic, readonly) NSData* remoteAddressData;
/**
* Returns the address of the remote peer (i.e. client) of the connection
* as a dotted string.
*/
@property(nonatomic, readonly) NSString* remoteAddressString;
/**
* Returns the total number of bytes received from the remote peer (i.e. client)
* so far.
*/
@property(nonatomic, readonly) NSUInteger totalBytesRead;
/**
* Returns the total number of bytes sent to the remote peer (i.e. client) so far.
*/
@property(nonatomic, readonly) NSUInteger totalBytesWritten;
@end
/**
* Hooks to customize the behavior of GCDWebServer HTTP connections.
*
* @warning These methods can be called on any GCD thread.
* Be sure to also call "super" when overriding them.
*/
@interface GCDWebServerConnection (Subclassing)
/**
* This method is called when the connection is opened.
* Return NO to reject the connection e.g. after validating the local
* or remote address.
*/
- (BOOL)open;
/**
* This method is called whenever data has been received
* from the remote peer (i.e. client).
*/
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length;
/**
* This method is called whenever data has been sent
* to the remote peer (i.e. client).
*/
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length;
/**
* Assuming a valid request was received, this method is called before
* the request is processed.
*
* Return a non-nil GCDWebServerResponse to bypass the request processing entirely.
*
* The default implementation checks for HTTP authentication if applicable
* and returns a barebone 401 status code response if authentication failed.
*/
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request;
/**
* Assuming a valid request was received and -preflightRequest: returned nil,
* this method is called to process the request.
*/
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block;
/**
* Assuming a valid request was received and either -preflightRequest:
* or -processRequest:withBlock: returned a non-nil GCDWebServerResponse,
* this method is called to override the response.
*
* You can either modify the current response and return it, or return a
* completely different one.
*
* The default implementation replaces any response matching the "ETag" or
* "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304)
* one.
*/
- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request;
/**
* This method is called if any error happens while validing or processing
* the request or no GCDWebServerResponse is generated.
*
* @warning If the request was invalid (e.g. the HTTP headers were malformed),
* the "request" argument will be nil.
*/
- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode;
/**
* Called when the connection is closed.
*/
- (void)close;
@end

View File

@@ -27,20 +27,19 @@
#import <TargetConditionals.h>
#import <netdb.h>
#if !TARGET_OS_IPHONE
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
#import <libkern/OSAtomic.h>
#endif
#import "GCDWebServerPrivate.h"
#define kHeadersReadBuffer 1024
#define kHeadersReadCapacity (1 * 1024)
#define kBodyReadCapacity (256 * 1024)
typedef void (^ReadBufferCompletionBlock)(dispatch_data_t buffer);
typedef void (^ReadDataCompletionBlock)(NSData* data);
typedef void (^ReadDataCompletionBlock)(BOOL success);
typedef void (^ReadHeadersCompletionBlock)(NSData* extraData);
typedef void (^ReadBodyCompletionBlock)(BOOL success);
typedef void (^WriteBufferCompletionBlock)(BOOL success);
typedef void (^WriteDataCompletionBlock)(BOOL success);
typedef void (^WriteHeadersCompletionBlock)(BOOL success);
typedef void (^WriteBodyCompletionBlock)(BOOL success);
@@ -49,7 +48,8 @@ static NSData* _CRLFData = nil;
static NSData* _CRLFCRLFData = nil;
static NSData* _continueData = nil;
static NSData* _lastChunkData = nil;
#if !TARGET_OS_IPHONE
static NSString* _digestAuthenticationNonce = nil;
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
static int32_t _connectionCounter = 0;
#endif
@@ -70,7 +70,8 @@ static int32_t _connectionCounter = 0;
GCDWebServerResponse* _response;
NSInteger _statusCode;
#if !TARGET_OS_IPHONE
BOOL _opened;
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
NSUInteger _connectionIndex;
NSString* _requestPath;
int _requestFD;
@@ -82,87 +83,50 @@ static int32_t _connectionCounter = 0;
@implementation GCDWebServerConnection (Read)
- (void)_readBufferWithLength:(NSUInteger)length completionBlock:(ReadBufferCompletionBlock)block {
- (void)_readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
dispatch_read(_socket, length, kGCDWebServerGCDQueue, ^(dispatch_data_t buffer, int error) {
@autoreleasepool {
if (error == 0) {
size_t size = dispatch_data_get_size(buffer);
if (size > 0) {
LOG_DEBUG(@"Connection received %zu bytes on socket %i", size, _socket);
_bytesRead += size;
[self didUpdateBytesRead];
#if !TARGET_OS_IPHONE
if (_requestFD > 0) {
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
return (write(_requestFD, chunkBytes, chunkSize) == (ssize_t)chunkSize);
});
if (!success) {
LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
close(_requestFD);
_requestFD = 0;
}
}
#endif
block(buffer);
NSUInteger originalLength = data.length;
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
[data appendBytes:chunkBytes length:chunkSize];
return true;
});
[self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
block(YES);
} else {
if (_bytesRead > 0) {
LOG_ERROR(@"No more data available on socket %i", _socket);
} else {
LOG_WARNING(@"No data received from socket %i", _socket);
}
block(NULL);
block(NO);
}
} else {
LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error);
block(NULL);
block(NO);
}
}
});
}
- (void)_readDataWithCompletionBlock:(ReadDataCompletionBlock)block {
[self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
if (buffer) {
NSMutableData* data = [[NSMutableData alloc] initWithCapacity:dispatch_data_get_size(buffer)];
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
[data appendBytes:chunkBytes length:chunkSize];
return true;
});
block(data);
ARC_RELEASE(data);
} else {
block(nil);
}
}];
}
- (void)_readHeadersWithCompletionBlock:(ReadHeadersCompletionBlock)block {
- (void)_readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
DCHECK(_requestMessage);
[self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
[self _readData:headersData withLength:NSUIntegerMax completionBlock:^(BOOL success) {
if (buffer) {
NSMutableData* data = [NSMutableData dataWithCapacity:kHeadersReadBuffer];
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
[data appendBytes:chunkBytes length:chunkSize];
return true;
});
NSRange range = [data rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, data.length)];
if (success) {
NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
if (range.location == NSNotFound) {
if (CFHTTPMessageAppendBytes(_requestMessage, data.bytes, data.length)) {
[self _readHeadersWithCompletionBlock:block];
} else {
LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
block(nil);
}
[self _readHeaders:headersData withCompletionBlock:block];
} else {
NSUInteger length = range.location + range.length;
if (CFHTTPMessageAppendBytes(_requestMessage, data.bytes, length)) {
if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
block([data subdataWithRange:NSMakeRange(length, data.length - length)]);
block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
} else {
LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
block(nil);
@@ -181,27 +145,21 @@ static int32_t _connectionCounter = 0;
- (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
[self _readBufferWithLength:length completionBlock:^(dispatch_data_t buffer) {
NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
[self _readData:bodyData withLength:length completionBlock:^(BOOL success) {
if (buffer) {
if (dispatch_data_get_size(buffer) <= length) {
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
NSData* data = [NSData dataWithBytesNoCopy:(void*)chunkBytes length:chunkSize freeWhenDone:NO];
NSError* error = nil;
if (![_request performWriteData:data error:&error]) {
LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
return false;
}
return true;
});
if (success) {
NSUInteger remainingLength = length - dispatch_data_get_size(buffer);
if (success) {
if (bodyData.length <= length) {
NSError* error = nil;
if ([_request performWriteData:bodyData error:&error]) {
NSUInteger remainingLength = length - bodyData.length;
if (remainingLength) {
[self _readBodyWithRemainingLength:remainingLength completionBlock:block];
} else {
block(YES);
}
} else {
LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
block(NO);
}
} else {
@@ -214,6 +172,7 @@ static int32_t _connectionCounter = 0;
}
}];
ARC_RELEASE(bodyData);
}
static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
@@ -269,13 +228,9 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
}
}
[self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
[self _readData:chunkData withLength:NSUIntegerMax completionBlock:^(BOOL success) {
if (buffer) {
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
[chunkData appendBytes:chunkBytes length:chunkSize];
return true;
});
if (success) {
[self _readNextBodyChunk:chunkData completionBlock:block];
} else {
block(NO);
@@ -288,44 +243,6 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
@implementation GCDWebServerConnection (Write)
- (void)_writeBuffer:(dispatch_data_t)buffer withCompletionBlock:(WriteBufferCompletionBlock)block {
size_t size = dispatch_data_get_size(buffer);
#if !TARGET_OS_IPHONE
ARC_DISPATCH_RETAIN(buffer);
#endif
dispatch_write(_socket, buffer, kGCDWebServerGCDQueue, ^(dispatch_data_t data, int error) {
@autoreleasepool {
if (error == 0) {
DCHECK(data == NULL);
LOG_DEBUG(@"Connection sent %zu bytes on socket %i", size, _socket);
_bytesWritten += size;
[self didUpdateBytesWritten];
#if !TARGET_OS_IPHONE
if (_responseFD > 0) {
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
return (write(_responseFD, chunkBytes, chunkSize) == (ssize_t)chunkSize);
});
if (!success) {
LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
close(_responseFD);
_responseFD = 0;
}
}
#endif
block(YES);
} else {
LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
block(NO);
}
}
#if !TARGET_OS_IPHONE
ARC_DISPATCH_RELEASE(buffer);
#endif
});
}
- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
#if !__has_feature(objc_arc)
[data retain];
@@ -337,15 +254,28 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
[data release];
#endif
});
[self _writeBuffer:buffer withCompletionBlock:block];
dispatch_write(_socket, buffer, kGCDWebServerGCDQueue, ^(dispatch_data_t remainingData, int error) {
@autoreleasepool {
if (error == 0) {
DCHECK(remainingData == NULL);
[self didWriteBytes:data.bytes length:data.length];
block(YES);
} else {
LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
block(NO);
}
}
});
ARC_DISPATCH_RELEASE(buffer);
}
- (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
DCHECK(_responseMessage);
CFDataRef message = CFHTTPMessageCopySerializedMessage(_responseMessage);
[self _writeData:(ARC_BRIDGE NSData*)message withCompletionBlock:block];
CFRelease(message);
CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage);
[self _writeData:(ARC_BRIDGE NSData*)data withCompletionBlock:block];
CFRelease(data);
}
- (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
@@ -428,13 +358,18 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
if (_lastChunkData == nil) {
_lastChunkData = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5];
}
if (_digestAuthenticationNonce == nil) {
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
_digestAuthenticationNonce = ARC_RETAIN(GCDWebServerComputeMD5Digest(@"%@", ARC_BRIDGE_RELEASE(CFUUIDCreateString(kCFAllocatorDefault, uuid))));
CFRelease(uuid);
}
}
- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
_statusCode = statusCode;
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close"));
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (ARC_BRIDGE CFStringRef)[[_server class] serverName]);
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (ARC_BRIDGE CFStringRef)_server.serverName);
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (ARC_BRIDGE CFStringRef)GCDWebServerFormatRFC822([NSDate date]));
}
@@ -443,20 +378,23 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
DCHECK(_responseMessage == NULL);
BOOL hasBody = NO;
GCDWebServerResponse* response = [self processRequest:_request withBlock:_handler.processBlock];
GCDWebServerResponse* response = [self preflightRequest:_request];
if (!response) {
response = [self processRequest:_request withBlock:_handler.processBlock];
}
if (response) {
response = [self replaceResponse:response forRequest:_request];
if (response) {
if ([response hasBody]) {
[response prepareForReading];
hasBody = !_virtualHEAD;
}
NSError* error = nil;
if (hasBody && ![response performOpen:&error]) {
LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error);
} else {
_response = ARC_RETAIN(response);
}
response = [self overrideResponse:response forRequest:_request];
}
if (response) {
if ([response hasBody]) {
[response prepareForReading];
hasBody = !_virtualHEAD;
}
NSError* error = nil;
if (hasBody && ![response performOpen:&error]) {
LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error);
} else {
_response = ARC_RETAIN(response);
}
}
@@ -575,79 +513,78 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
- (void)_readRequestHeaders {
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
[self _readHeadersWithCompletionBlock:^(NSData* extraData) {
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
[self _readHeaders:headersData withCompletionBlock:^(NSData* extraData) {
if (extraData) {
NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase
DCHECK(requestMethod);
if ([[_server class] shouldAutomaticallyMapHEADToGET] && [requestMethod isEqualToString:@"HEAD"]) {
if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
requestMethod = @"GET";
_virtualHEAD = YES;
}
NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(_requestMessage));
DCHECK(requestURL);
NSString* requestPath = GCDWebServerUnescapeURLString(ARC_BRIDGE_RELEASE(CFURLCopyPath((CFURLRef)requestURL))); // Don't use -[NSURL path] which strips the ending slash
DCHECK(requestPath);
NSDictionary* requestQuery = nil;
NSString* queryString = ARC_BRIDGE_RELEASE(CFURLCopyQueryString((CFURLRef)requestURL, NULL)); // Don't use -[NSURL query] to make sure query is not unescaped;
if (queryString.length) {
requestQuery = GCDWebServerParseURLEncodedForm(queryString);
DCHECK(requestQuery);
}
NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(ARC_BRIDGE_RELEASE(CFURLCopyPath((CFURLRef)requestURL))) : nil; // Don't use -[NSURL path] which strips the ending slash
NSString* queryString = requestURL ? ARC_BRIDGE_RELEASE(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
NSDictionary* requestHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
DCHECK(requestHeaders);
for (_handler in _server.handlers) {
_request = ARC_RETAIN(_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery));
if (_request) {
break;
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
for (_handler in _server.handlers) {
_request = ARC_RETAIN(_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery));
if (_request) {
break;
}
}
}
if (_request) {
if ([_request hasBody]) {
[_request prepareForWriting];
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
NSString* expectHeader = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyHeaderFieldValue(_requestMessage, CFSTR("Expect")));
if (expectHeader) {
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) {
[self _writeData:_continueData withCompletionBlock:^(BOOL success) {
if (success) {
if (_request.usesChunkedTransferEncoding) {
[self _readChunkedBodyWithInitialData:extraData];
} else {
[self _readBodyWithLength:_request.contentLength initialData:extraData];
if (_request) {
if ([_request hasBody]) {
[_request prepareForWriting];
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
if (expectHeader) {
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
[self _writeData:_continueData withCompletionBlock:^(BOOL success) {
if (success) {
if (_request.usesChunkedTransferEncoding) {
[self _readChunkedBodyWithInitialData:extraData];
} else {
[self _readBodyWithLength:_request.contentLength initialData:extraData];
}
}
}
}];
}];
} else {
LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
}
} else {
LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
if (_request.usesChunkedTransferEncoding) {
[self _readChunkedBodyWithInitialData:extraData];
} else {
[self _readBodyWithLength:_request.contentLength initialData:extraData];
}
}
} else {
if (_request.usesChunkedTransferEncoding) {
[self _readChunkedBodyWithInitialData:extraData];
} else {
[self _readBodyWithLength:_request.contentLength initialData:extraData];
}
LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
}
} else {
LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
[self _processRequest];
}
} else {
[self _processRequest];
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
DCHECK(_request);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_MethodNotAllowed];
}
} else {
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
DCHECK(_request);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_MethodNotAllowed];
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
DNOT_REACHED();
}
} else {
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
}
}];
ARC_RELEASE(headersData);
}
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
@@ -656,8 +593,18 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
_localAddress = ARC_RETAIN(localAddress);
_remoteAddress = ARC_RETAIN(remoteAddress);
_socket = socket;
LOG_DEBUG(@"Did open connection on socket %i", _socket);
[self open];
[_server willStartConnection:self];
if (![self open]) {
close(_socket);
ARC_RELEASE(self);
return nil;
}
_opened = YES;
[self _readRequestHeaders];
}
return self;
}
@@ -684,8 +631,18 @@ static NSString* _StringFromAddressData(NSData* data) {
}
- (void)dealloc {
[self close];
int result = close(_socket);
if (result != 0) {
LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno);
} else {
LOG_DEBUG(@"Did close connection on socket %i", _socket);
}
if (_opened) {
[self close];
}
[_server didEndConnection:self];
ARC_RELEASE(_server);
ARC_RELEASE(_localAddress);
ARC_RELEASE(_remoteAddress);
@@ -700,7 +657,7 @@ static NSString* _StringFromAddressData(NSData* data) {
}
ARC_RELEASE(_response);
#if !TARGET_OS_IPHONE
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
ARC_RELEASE(_requestPath);
ARC_RELEASE(_responsePath);
#endif
@@ -712,10 +669,8 @@ static NSString* _StringFromAddressData(NSData* data) {
@implementation GCDWebServerConnection (Subclassing)
- (void)open {
LOG_DEBUG(@"Did open connection on socket %i", _socket);
#if !TARGET_OS_IPHONE
- (BOOL)open {
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
if (_server.recordingEnabled) {
_connectionIndex = OSAtomicIncrement32(&_connectionCounter);
@@ -729,15 +684,84 @@ static NSString* _StringFromAddressData(NSData* data) {
}
#endif
[self _readRequestHeaders];
return YES;
}
- (void)didUpdateBytesRead {
;
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket);
_bytesRead += length;
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
close(_requestFD);
_requestFD = 0;
}
#endif
}
- (void)didUpdateBytesWritten {
;
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket);
_bytesWritten += length;
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
close(_responseFD);
_responseFD = 0;
}
#endif
}
// https://tools.ietf.org/html/rfc2617
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request {
LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
GCDWebServerResponse* response = nil;
if (_server.authenticationBasicAccounts) {
__block BOOL authenticated = NO;
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
if ([authorizationHeader hasPrefix:@"Basic "]) {
NSString* basicAccount = [authorizationHeader substringFromIndex:6];
[_server.authenticationBasicAccounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* digest, BOOL* stop) {
if ([basicAccount isEqualToString:digest]) {
authenticated = YES;
*stop = YES;
}
}];
}
if (!authenticated) {
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
[response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"];
}
} else if (_server.authenticationDigestAccounts) {
BOOL authenticated = NO;
BOOL isStaled = NO;
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
if ([authorizationHeader hasPrefix:@"Digest "]) {
NSString* realm = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"realm");
if ([realm isEqualToString:_server.authenticationRealm]) {
NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce");
if ([nonce isEqualToString:_digestAuthenticationNonce]) {
NSString* username = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"username");
NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri");
NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response");
NSString* ha1 = [_server.authenticationDigestAccounts objectForKey:username];
NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri); // We cannot use "request.path" as the query string is required
NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2);
if ([actualResponse isEqualToString:expectedResponse]) {
authenticated = YES;
}
} else if (nonce.length) {
isStaled = YES;
}
}
}
if (!authenticated) {
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
[response setValue:[NSString stringWithFormat:@"Digest realm=\"%@\", nonce=\"%@\"%@", _server.authenticationRealm, _digestAuthenticationNonce, isStaled ? @", stale=TRUE" : @""] forAdditionalHeader:@"WWW-Authenticate"]; // TODO: Support Quality of Protection ("qop")
}
}
return response;
}
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {
@@ -767,7 +791,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
return NO;
}
- (GCDWebServerResponse*)replaceResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request {
- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request {
if ((response.statusCode >= 200) && (response.statusCode < 300) && _CompareResources(response.eTag, request.ifNoneMatch, response.lastModifiedDate, request.ifModifiedSince)) {
NSInteger code = [request.method isEqualToString:@"HEAD"] || [request.method isEqualToString:@"GET"] ? kGCDWebServerHTTPStatusCode_NotModified : kGCDWebServerHTTPStatusCode_PreconditionFailed;
GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code];
@@ -791,14 +815,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
}
- (void)close {
int result = close(_socket);
if (result != 0) {
LOG_ERROR(@"Failed closing socket %i for connection (%i): %s", _socket, errno, strerror(errno));
} else {
LOG_DEBUG(@"Did close connection on socket %i", _socket);
}
#if !TARGET_OS_IPHONE
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
if (_requestPath) {
BOOL success = NO;
NSError* error = nil;
@@ -829,6 +846,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
unlink([_responsePath fileSystemRepresentation]);
}
#endif
if (_request) {
LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
} else {

View File

@@ -0,0 +1,100 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/Foundation.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Converts a file extension to the corresponding MIME type.
* If there is no match, "application/octet-stream" is returned.
*/
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension);
/**
* Add percent-escapes to a string so it can be used in a URL.
* The legal characters ":@/?&=+" are also escaped to ensure compatibility
* with URL encoded forms and URL queries.
*/
NSString* GCDWebServerEscapeURLString(NSString* string);
/**
* Unescapes a URL percent-encoded string.
*/
NSString* GCDWebServerUnescapeURLString(NSString* string);
/**
* Extracts the unescaped names and values
* from a "application/x-www-form-urlencoded" form.
* http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
*/
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
/**
* OS X: Returns the IPv4 address as a dotted string of the primary connected
* service or nil if not available.
* iOS: Returns the IPv4 address as a dotted string of the WiFi interface
* if connected or nil otherwise.
*/
NSString* GCDWebServerGetPrimaryIPv4Address();
/**
* Converts a date into a string using RFC822 formatting.
* https://tools.ietf.org/html/rfc822#section-5
* https://tools.ietf.org/html/rfc1123#section-5.2.14
*/
NSString* GCDWebServerFormatRFC822(NSDate* date);
/**
* Converts a RFC822 formatted string into a date.
* https://tools.ietf.org/html/rfc822#section-5
* https://tools.ietf.org/html/rfc1123#section-5.2.14
*
* @warning Timezones are not supported at this time.
*/
NSDate* GCDWebServerParseRFC822(NSString* string);
/**
* Converts a date into a string using IOS 8601 formatting.
* http://tools.ietf.org/html/rfc3339#section-5.6
*/
NSString* GCDWebServerFormatISO8601(NSDate* date);
/**
* Converts a ISO 8601 formatted string into a date.
* http://tools.ietf.org/html/rfc3339#section-5.6
*
* @warning Only "calendar" variant is supported at this time and timezones
* are not supported either.
*/
NSDate* GCDWebServerParseISO8601(NSString* string);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,284 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IPHONE
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <SystemConfiguration/SystemConfiguration.h>
#endif
#import <CommonCrypto/CommonDigest.h>
#import <ifaddrs.h>
#import <net/if.h>
#import <netdb.h>
#import "GCDWebServerPrivate.h"
static NSDateFormatter* _dateFormatterRFC822 = nil;
static NSDateFormatter* _dateFormatterISO8601 = nil;
static dispatch_queue_t _dateFormatterQueue = NULL;
// TODO: Handle RFC 850 and ANSI C's asctime() format
void GCDWebServerInitializeFunctions() {
DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread
if (_dateFormatterRFC822 == nil) {
_dateFormatterRFC822 = [[NSDateFormatter alloc] init];
_dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
_dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
_dateFormatterRFC822.locale = ARC_AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]);
DCHECK(_dateFormatterRFC822);
}
if (_dateFormatterISO8601 == nil) {
_dateFormatterISO8601 = [[NSDateFormatter alloc] init];
_dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
_dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'";
_dateFormatterISO8601.locale = ARC_AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]);
DCHECK(_dateFormatterISO8601);
}
if (_dateFormatterQueue == NULL) {
_dateFormatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
DCHECK(_dateFormatterQueue);
}
}
NSString* GCDWebServerNormalizeHeaderValue(NSString* value) {
if (value) {
NSRange range = [value rangeOfString:@";"]; // Assume part before ";" separator is case-insensitive
if (range.location != NSNotFound) {
value = [[[value substringToIndex:range.location] lowercaseString] stringByAppendingString:[value substringFromIndex:range.location]];
} else {
value = [value lowercaseString];
}
}
return value;
}
NSString* GCDWebServerTruncateHeaderValue(NSString* value) {
NSRange range = [value rangeOfString:@";"];
return range.location != NSNotFound ? [value substringToIndex:range.location] : value;
}
NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) {
NSString* parameter = nil;
NSScanner* scanner = [[NSScanner alloc] initWithString:value];
[scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive
NSString* string = [NSString stringWithFormat:@"%@=", name];
if ([scanner scanUpToString:string intoString:NULL]) {
[scanner scanString:string intoString:NULL];
if ([scanner scanString:@"\"" intoString:NULL]) {
[scanner scanUpToString:@"\"" intoString:&parameter];
} else {
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&parameter];
}
}
ARC_RELEASE(scanner);
return parameter;
}
// http://www.w3schools.com/tags/ref_charactersets.asp
NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) {
NSStringEncoding encoding = kCFStringEncodingInvalidId;
if (charset) {
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset));
}
return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding);
}
NSString* GCDWebServerFormatRFC822(NSDate* date) {
__block NSString* string;
dispatch_sync(_dateFormatterQueue, ^{
string = [_dateFormatterRFC822 stringFromDate:date];
});
return string;
}
NSDate* GCDWebServerParseRFC822(NSString* string) {
__block NSDate* date;
dispatch_sync(_dateFormatterQueue, ^{
date = [_dateFormatterRFC822 dateFromString:string];
});
return date;
}
NSString* GCDWebServerFormatISO8601(NSDate* date) {
__block NSString* string;
dispatch_sync(_dateFormatterQueue, ^{
string = [_dateFormatterISO8601 stringFromDate:date];
});
return string;
}
NSDate* GCDWebServerParseISO8601(NSString* string) {
__block NSDate* date;
dispatch_sync(_dateFormatterQueue, ^{
date = [_dateFormatterISO8601 dateFromString:string];
});
return date;
}
BOOL GCDWebServerIsTextContentType(NSString* type) {
return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]);
}
NSString* GCDWebServerDescribeData(NSData* data, NSString* type) {
if (GCDWebServerIsTextContentType(type)) {
NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset");
NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)];
if (string) {
return ARC_AUTORELEASE(string);
}
}
return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length];
}
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
static NSDictionary* _overrides = nil;
if (_overrides == nil) {
_overrides = [[NSDictionary alloc] initWithObjectsAndKeys:
@"text/css", @"css",
nil];
}
NSString* mimeType = nil;
extension = [extension lowercaseString];
if (extension.length) {
mimeType = [_overrides objectForKey:extension];
if (mimeType == nil) {
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (ARC_BRIDGE CFStringRef)extension, NULL);
if (uti) {
mimeType = ARC_BRIDGE_RELEASE(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
CFRelease(uti);
}
}
}
return mimeType ? mimeType : kGCDWebServerDefaultMimeType;
}
NSString* GCDWebServerEscapeURLString(NSString* string) {
return ARC_BRIDGE_RELEASE(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8));
}
NSString* GCDWebServerUnescapeURLString(NSString* string) {
return ARC_BRIDGE_RELEASE(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8));
}
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
NSScanner* scanner = [[NSScanner alloc] initWithString:form];
[scanner setCharactersToBeSkipped:nil];
while (1) {
NSString* key = nil;
if (![scanner scanUpToString:@"=" intoString:&key] || [scanner isAtEnd]) {
break;
}
[scanner setScanLocation:([scanner scanLocation] + 1)];
NSString* value = nil;
if (![scanner scanUpToString:@"&" intoString:&value]) {
break;
}
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
if (key && value) {
[parameters setObject:GCDWebServerUnescapeURLString(value) forKey:GCDWebServerUnescapeURLString(key)];
} else {
DNOT_REACHED();
}
if ([scanner isAtEnd]) {
break;
}
[scanner setScanLocation:([scanner scanLocation] + 1)];
}
ARC_RELEASE(scanner);
return parameters;
}
NSString* GCDWebServerGetPrimaryIPv4Address() {
NSString* address = nil;
#if TARGET_OS_IPHONE
#if !TARGET_IPHONE_SIMULATOR
const char* primaryInterface = "en0"; // WiFi interface on iOS
#endif
#else
const char* primaryInterface = NULL;
SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL);
if (store) {
CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4"));
if (info) {
primaryInterface = [[NSString stringWithString:[(ARC_BRIDGE NSDictionary*)info objectForKey:@"PrimaryInterface"]] UTF8String];
CFRelease(info);
}
CFRelease(store);
}
if (primaryInterface == NULL) {
primaryInterface = "lo0";
}
#endif
struct ifaddrs* list;
if (getifaddrs(&list) >= 0) {
for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) {
#if TARGET_IPHONE_SIMULATOR
if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator
#else
if (strcmp(ifap->ifa_name, primaryInterface))
#endif
{
continue;
}
if ((ifap->ifa_flags & IFF_UP) && (ifap->ifa_addr->sa_family == AF_INET)) {
char buffer[NI_MAXHOST];
if (getnameinfo(ifap->ifa_addr, ifap->ifa_addr->sa_len, buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST | NI_NOFQDN) >= 0) {
address = [NSString stringWithUTF8String:buffer];
}
break;
}
}
freeifaddrs(list);
}
return address;
}
NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) {
va_list arguments;
va_start(arguments, format);
const char* string = [ARC_AUTORELEASE([[NSString alloc] initWithFormat:format arguments:arguments]) UTF8String];
va_end(arguments);
unsigned char md5[CC_MD5_DIGEST_LENGTH];
CC_MD5(string, (CC_LONG)strlen(string), md5);
char buffer[2 * CC_MD5_DIGEST_LENGTH + 1];
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) {
unsigned char byte = md5[i];
unsigned char byteHi = (byte & 0xF0) >> 4;
buffer[2 * i + 0] = byteHi >= 10 ? 'a' + byteHi - 10 : '0' + byteHi;
unsigned char byteLo = byte & 0x0F;
buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo;
}
buffer[2 * CC_MD5_DIGEST_LENGTH] = 0;
return [NSString stringWithUTF8String:buffer];
}

View File

@@ -30,12 +30,18 @@
#import <Foundation/Foundation.h>
/**
* Convenience constants for "informational" HTTP status codes.
*/
typedef NS_ENUM(NSInteger, GCDWebServerInformationalHTTPStatusCode) {
kGCDWebServerHTTPStatusCode_Continue = 100,
kGCDWebServerHTTPStatusCode_SwitchingProtocols = 101,
kGCDWebServerHTTPStatusCode_Processing = 102
};
/**
* Convenience constants for "successful" HTTP status codes.
*/
typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) {
kGCDWebServerHTTPStatusCode_OK = 200,
kGCDWebServerHTTPStatusCode_Created = 201,
@@ -48,6 +54,9 @@ typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) {
kGCDWebServerHTTPStatusCode_AlreadyReported = 208
};
/**
* Convenience constants for "redirection" HTTP status codes.
*/
typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) {
kGCDWebServerHTTPStatusCode_MultipleChoices = 300,
kGCDWebServerHTTPStatusCode_MovedPermanently = 301,
@@ -59,6 +68,9 @@ typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) {
kGCDWebServerHTTPStatusCode_PermanentRedirect = 308
};
/**
* Convenience constants for "client error" HTTP status codes.
*/
typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) {
kGCDWebServerHTTPStatusCode_BadRequest = 400,
kGCDWebServerHTTPStatusCode_Unauthorized = 401,
@@ -87,6 +99,9 @@ typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) {
kGCDWebServerHTTPStatusCode_RequestHeaderFieldsTooLarge = 431
};
/**
* Convenience constants for "server error" HTTP status codes.
*/
typedef NS_ENUM(NSInteger, GCDWebServerServerErrorHTTPStatusCode) {
kGCDWebServerHTTPStatusCode_InternalServerError = 500,
kGCDWebServerHTTPStatusCode_NotImplemented = 501,

View File

@@ -54,7 +54,9 @@
#endif
#import "GCDWebServerHTTPStatusCodes.h"
#import "GCDWebServerFunctions.h"
#import "GCDWebServer.h"
#import "GCDWebServerConnection.h"
#import "GCDWebServerDataRequest.h"
@@ -65,7 +67,7 @@
#import "GCDWebServerDataResponse.h"
#import "GCDWebServerErrorResponse.h"
#import "GCDWebServerFileResponse.h"
#import "GCDWebServerStreamingResponse.h"
#import "GCDWebServerStreamedResponse.h"
#ifdef __GCDWEBSERVER_LOGGING_HEADER__
@@ -112,11 +114,14 @@ static inline BOOL GCDWebServerIsValidByteRange(NSRange range) {
return ((range.location != NSNotFound) || (range.length > 0));
}
extern void GCDWebServerInitializeFunctions();
extern NSString* GCDWebServerNormalizeHeaderValue(NSString* value);
extern NSString* GCDWebServerTruncateHeaderValue(NSString* value);
extern NSString* GCDWebServerExtractHeaderValueParameter(NSString* header, NSString* attribute);
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
extern BOOL GCDWebServerIsTextContentType(NSString* type);
extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1,2);
@interface GCDWebServerConnection ()
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
@@ -124,6 +129,13 @@ extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
@interface GCDWebServer ()
@property(nonatomic, readonly) NSArray* handlers;
@property(nonatomic, readonly) NSString* serverName;
@property(nonatomic, readonly) NSString* authenticationRealm;
@property(nonatomic, readonly) NSDictionary* authenticationBasicAccounts;
@property(nonatomic, readonly) NSDictionary* authenticationDigestAccounts;
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
- (void)willStartConnection:(GCDWebServerConnection*)connection;
- (void)didEndConnection:(GCDWebServerConnection*)connection;
@end
@interface GCDWebServerHandler : NSObject

View File

@@ -0,0 +1,166 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/Foundation.h>
/**
* This protocol is used by the GCDWebServerConnection to communicate with
* the GCDWebServerRequest and write the received HTTP body data.
*
* Note that multiple GCDWebServerBodyWriter objects can be chained together
* internally e.g. to automatically decode gzip encoded content before
* passing it on to the GCDWebServerRequest.
*
* @warning These methods can be called on any GCD thread.
*/
@protocol GCDWebServerBodyWriter <NSObject>
/**
* This method is called before any body data is received.
*
* It should return YES on success or NO on failure and set the "error" argument
* which is guaranteed to be non-NULL.
*/
- (BOOL)open:(NSError**)error;
/**
* This method is called whenever body data has been received.
*
* It should return YES on success or NO on failure and set the "error" argument
* which is guaranteed to be non-NULL.
*/
- (BOOL)writeData:(NSData*)data error:(NSError**)error;
/**
* This method is called after all body data has been received.
*
* It should return YES on success or NO on failure and set the "error" argument
* which is guaranteed to be non-NULL.
*/
- (BOOL)close:(NSError**)error;
@end
/**
* The GCDWebServerRequest class is instantiated by the GCDWebServerConnection
* after the HTTP headers have been received. Each instance wraps a single HTTP
* request. If a body is present, the methods from the GCDWebServerBodyWriter
* protocol will be called by the GCDWebServerConnection to receive it.
*
* The default implementation of the GCDWebServerBodyWriter protocol on the class
* simply ignores the body data.
*
* @warning GCDWebServerRequest instances can be created and used on any GCD thread.
*/
@interface GCDWebServerRequest : NSObject <GCDWebServerBodyWriter>
/**
* Returns the HTTP method for the request.
*/
@property(nonatomic, readonly) NSString* method;
/**
* Returns the URL for the request.
*/
@property(nonatomic, readonly) NSURL* URL;
/**
* Returns the HTTP headers for the request.
*/
@property(nonatomic, readonly) NSDictionary* headers;
/**
* Returns the path component of the URL for the request.
*/
@property(nonatomic, readonly) NSString* path;
/**
* Returns the parsed and unescaped query component of the URL for the request.
*
* @warning This property will be nil if there is no query in the URL.
*/
@property(nonatomic, readonly) NSDictionary* query;
/**
* Returns the content type for the body of the request (this property is
* automatically parsed from the HTTP headers).
*
* This property will be nil if the request has no body or set to
* "application/octet-stream" if a body is present but there was no
* "Content-Type" header.
*/
@property(nonatomic, readonly) NSString* contentType;
/**
* Returns the content length for the body of the request (this property is
* automatically parsed from the HTTP headers).
*
* This property will be set to "NSNotFound" if the request has no body or
* if there is a body but no "Content-Length" header, typically because
* chunked transfer encoding is used.
*/
@property(nonatomic, readonly) NSUInteger contentLength;
/**
* Returns the parsed "If-Modified-Since" header or nil if absent or malformed.
*/
@property(nonatomic, readonly) NSDate* ifModifiedSince;
/**
* Returns the parsed "If-None-Match" header or nil if absent or malformed.
*/
@property(nonatomic, readonly) NSString* ifNoneMatch;
/**
* Returns the parsed "Range" header or (NSNotFound, 0) if absent or malformed.
* The range will be set to (offset, length) if expressed from the beginning
* of the body, or (NSNotFound, -length) if expressed from the end of the body.
*/
@property(nonatomic, readonly) NSRange byteRange;
/**
* Indicates if the client supports gzip content encoding (this property is
* automatically parsed from the HTTP headers).
*/
@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query;
/**
* Convenience method that checks if the contentType property is defined.
*/
- (BOOL)hasBody;
/**
* Convenience method that checks if the byteRange property is defined.
*/
- (BOOL)hasByteRange;
@end

View File

@@ -0,0 +1,188 @@
/*
Copyright (c) 2012-2014, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/Foundation.h>
/**
* This protocol is used by the GCDWebServerConnection to communicate with
* the GCDWebServerResponse and read the sent HTTP body data.
*
* Note that multiple GCDWebServerBodyReader objects can be chained together
* internally e.g. to automatically apply gzip encoding to the content before
* passing it on to the GCDWebServerResponse.
*
* @warning These methods can be called on any GCD thread.
*/
@protocol GCDWebServerBodyReader <NSObject>
/**
* This method is called before any body data is sent.
*
* It should return YES on success or NO on failure and set the "error" argument
* which is guaranteed to be non-NULL.
*/
- (BOOL)open:(NSError**)error;
/**
* This method is called whenever body data is ready to be sent.
*
* It should return a non-empty NSData if there is body data available,
* or an empty NSData there is no more body data, or nil on error and set
* the "error" argument which is guaranteed to be non-NULL.
*/
- (NSData*)readData:(NSError**)error;
/**
* This method is called after all body data has been sent.
*/
- (void)close;
@end
/**
* The GCDWebServerResponse class is used to wrap a single HTTP response.
* It is instantiated by the handler of the GCDWebServer that handled the request.
* If a body is present, the methods from the GCDWebServerBodyReader protocol
* will be called by the GCDWebServerConnection to retrieve it.
*
* The default implementation of the GCDWebServerBodyReader protocol
* on the class simply returns an empty body.
*
* @warning GCDWebServerResponse instances can be created and used on any GCD thread.
*/
@interface GCDWebServerResponse : NSObject <GCDWebServerBodyReader>
/**
* Sets the content type for the body of the response.
* This property must be set if a body is present.
*
* The default value is nil i.e. the response has no body.
*/
@property(nonatomic, copy) NSString* contentType;
/**
* Sets the content length for the body of the response. If a body is present
* but this property is set to "NSNotFound", this means the length of the body
* cannot be known ahead of time and chunked transfer encoding will be
* automatically enabled by the GCDWebServerConnection to comply with HTTP/1.1
* specifications.
*
* The default value is "NSNotFound" i.e. the response has no body or its length
* is undefined.
*/
@property(nonatomic) NSUInteger contentLength;
/**
* Sets the HTTP status code for the response.
*
* The default value is 200 i.e. "OK".
*/
@property(nonatomic) NSInteger statusCode;
/**
* Sets the caching hint for the response using the "Cache-Control" header.
* This value is expressed in seconds.
*
* The default value is 0 i.e. "no-cache".
*/
@property(nonatomic) NSUInteger cacheControlMaxAge;
/**
* Sets the last modified date for the response using the "Last-Modified" header.
*
* The default value is nil.
*/
@property(nonatomic, retain) NSDate* lastModifiedDate;
/**
* Sets the ETag for the response using the "ETag" header.
*
* The default value is nil.
*/
@property(nonatomic, copy) NSString* eTag;
/**
* Enables gzip encoding for the response body.
*
* The default value is NO.
*
* @warning Enabling gzip encoding will remove any "Content-Length" header
* since the length of the body is not known anymore. The client will still
* be able to determine the body length when connection is closed per
* HTTP/1.1 specifications.
*/
@property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled;
/**
* Creates an empty response.
*/
+ (instancetype)response;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)init;
/**
* Sets an additional HTTP header on the response.
* Pass a nil value to remove an additional header.
*
* @warning Do not attempt to override the primary headers used
* by GCDWebServerResponse e.g. "Content-Type" or "ETag".
*/
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header;
/**
* Convenience method that checks if the contentType property is defined.
*/
- (BOOL)hasBody;
@end
@interface GCDWebServerResponse (Extensions)
/**
* Creates a empty response with a specific HTTP status code.
*/
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode;
/**
* Creates an HTTP redirect response to a new URL.
*/
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
/**
* Initializes an empty response with a specific HTTP status code.
*/
- (instancetype)initWithStatusCode:(NSInteger)statusCode;
/**
* Initializes an HTTP redirect response to a new URL.
*/
- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
@end

View File

@@ -81,7 +81,7 @@
- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader {
if ((self = [super initWithResponse:response reader:reader])) {
response.contentLength = NSNotFound; // Make sure "Content-Length" header is not set since we don't know it (client will determine body length when connection is closed)
response.contentLength = NSNotFound; // Make sure "Content-Length" header is not set since we don't know it
[response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"];
}
return self;

View File

@@ -27,11 +27,34 @@
#import "GCDWebServerRequest.h"
/**
* The GCDWebServerDataRequest subclass of GCDWebServerRequest stores the body
* of the HTTP request in memory.
*/
@interface GCDWebServerDataRequest : GCDWebServerRequest
/**
* Returns the data for the request body.
*/
@property(nonatomic, readonly) NSData* data;
@end
@interface GCDWebServerDataRequest (Extensions)
@property(nonatomic, readonly) NSString* text; // Text encoding is extracted from Content-Type or defaults to UTF-8 - Returns nil on error
@property(nonatomic, readonly) id jsonObject; // Returns nil on error
/**
* Returns the data for the request body interpreted as text. If the content
* type of the body is not a text one, or if an error occurs, nil is returned.
*
* The text encoding used to interpret the data is extracted from the
* "Content-Type" header or defaults to UTF-8.
*/
@property(nonatomic, readonly) NSString* text;
/**
* Returns the data for the request body interpreted as a JSON object. If the
* content type of the body is not JSON, or if an error occurs, nil is returned.
*/
@property(nonatomic, readonly) id jsonObject;
@end

View File

@@ -27,6 +27,19 @@
#import "GCDWebServerRequest.h"
/**
* The GCDWebServerFileRequest subclass of GCDWebServerRequest stores the body
* of the HTTP request to a file on disk.
*/
@interface GCDWebServerFileRequest : GCDWebServerRequest
/**
* Returns the path to the temporary file containing the request body.
*
* @warning This temporary file will be automatically deleted when the
* GCDWebServerFileRequest is deallocated. If you want to preserve this file,
* you must move it to a different location beforehand.
*/
@property(nonatomic, readonly) NSString* temporaryPath;
@end

View File

@@ -27,23 +27,91 @@
#import "GCDWebServerRequest.h"
/**
* The GCDWebServerMultiPart class is an abstract class that wraps the content
* of a part.
*/
@interface GCDWebServerMultiPart : NSObject
@property(nonatomic, readonly) NSString* contentType; // Defaults to "text/plain" per specifications if undefined
/**
* Returns the content type retrieved from the part headers or "text/plain"
* if not available (per HTTP specifications).
*/
@property(nonatomic, readonly) NSString* contentType;
/**
* Returns the MIME type component of the content type for the part.
*/
@property(nonatomic, readonly) NSString* mimeType;
@end
/**
* The GCDWebServerMultiPartArgument subclass of GCDWebServerMultiPart wraps
* the content of a part as data in memory.
*/
@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart
/**
* Returns the data for the part.
*/
@property(nonatomic, readonly) NSData* data;
@property(nonatomic, readonly) NSString* string; // May be nil (only valid for text mime types)
/**
* Returns the data for the part interpreted as text. If the content
* type of the part is not a text one, or if an error occurs, nil is returned.
*
* The text encoding used to interpret the data is extracted from the
* "Content-Type" header or defaults to UTF-8.
*/
@property(nonatomic, readonly) NSString* string;
@end
/**
* The GCDWebServerMultiPartFile subclass of GCDWebServerMultiPart wraps
* the content of a part as a file on disk.
*/
@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart
@property(nonatomic, readonly) NSString* fileName; // May be nil
/**
* Returns the file name retrieved from the part headers.
*/
@property(nonatomic, readonly) NSString* fileName;
/**
* Returns the path to the temporary file containing the part data.
*
* @warning This temporary file will be automatically deleted when the
* GCDWebServerMultiPartFile is deallocated. If you want to preserve this file,
* you must move it to a different location beforehand.
*/
@property(nonatomic, readonly) NSString* temporaryPath;
@end
/**
* The GCDWebServerMultiPartFormRequest subclass of GCDWebServerRequest
* parses the body of the HTTP request as a multipart encoded form.
*/
@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest
/**
* Returns the argument parts from the multipart encoded form as
* name / GCDWebServerMultiPartArgument pairs.
*/
@property(nonatomic, readonly) NSDictionary* arguments;
/**
* Returns the files parts from the multipart encoded form as
* name / GCDWebServerMultiPartFile pairs.
*/
@property(nonatomic, readonly) NSDictionary* files;
/**
* Returns the MIME type for multipart encoded forms
* i.e. "multipart/form-data".
*/
+ (NSString*)mimeType;
@end

View File

@@ -228,27 +228,34 @@ static NSData* _dashNewlineData = nil;
_contentType = nil;
ARC_RELEASE(_tmpPath);
_tmpPath = nil;
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
const char* temp = "GET / HTTP/1.0\r\n";
CFHTTPMessageAppendBytes(message, (const UInt8*)temp, strlen(temp));
CFHTTPMessageAppendBytes(message, _parserData.bytes, range.location + range.length);
if (CFHTTPMessageIsHeaderComplete(message)) {
NSString* controlName = nil;
NSString* fileName = nil;
NSDictionary* headers = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(message));
NSString* contentDisposition = GCDWebServerNormalizeHeaderValue([headers objectForKey:@"Content-Disposition"]);
if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"form-data"]) {
controlName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name");
fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename");
NSString* headers = [[NSString alloc] initWithData:[_parserData subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding];
if (headers) {
for (NSString* header in [headers componentsSeparatedByString:@"\r\n"]) {
NSRange subRange = [header rangeOfString:@":"];
if (subRange.location != NSNotFound) {
NSString* name = [header substringToIndex:subRange.location];
NSString* value = [[header substringFromIndex:(subRange.location + subRange.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if ([name caseInsensitiveCompare:@"Content-Type"] == NSOrderedSame) {
_contentType = ARC_RETAIN(GCDWebServerNormalizeHeaderValue(value));
} else if ([name caseInsensitiveCompare:@"Content-Disposition"] == NSOrderedSame) {
NSString* contentDisposition = GCDWebServerNormalizeHeaderValue(value);
if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"form-data"]) {
_controlName = ARC_RETAIN(GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name"));
_fileName = ARC_RETAIN(GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"));
}
}
} else {
DNOT_REACHED();
}
}
_controlName = [controlName copy];
_fileName = [fileName copy];
_contentType = ARC_RETAIN(GCDWebServerNormalizeHeaderValue([headers objectForKey:@"Content-Type"]));
if (_contentType == nil) {
_contentType = @"text/plain";
}
ARC_RELEASE(headers);
} else {
LOG_ERROR(@"Failed decoding headers in part of 'multipart/form-data'");
DNOT_REACHED();
}
CFRelease(message);
if (_controlName) {
if (_fileName) {
NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];

View File

@@ -27,7 +27,25 @@
#import "GCDWebServerDataRequest.h"
/**
* The GCDWebServerURLEncodedFormRequest subclass of GCDWebServerRequest
* parses the body of the HTTP request as a URL encoded form using
* GCDWebServerParseURLEncodedForm().
*/
@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest
@property(nonatomic, readonly) NSDictionary* arguments; // Text encoding is extracted from Content-Type or defaults to UTF-8
/**
* Returns the unescaped names and values for the URL encoded form.
*
* The text encoding used to interpret the data is extracted from the
* "Content-Type" header or defaults to UTF-8.
*/
@property(nonatomic, readonly) NSDictionary* arguments;
/**
* Returns the MIME type for URL encoded forms
* i.e. "application/x-www-form-urlencoded".
*/
+ (NSString*)mimeType;
@end

View File

@@ -27,20 +27,81 @@
#import "GCDWebServerResponse.h"
/**
* The GCDWebServerDataResponse subclass of GCDWebServerResponse reads the body
* of the HTTP response from memory.
*/
@interface GCDWebServerDataResponse : GCDWebServerResponse
/**
* Creates a response with data in memory and a given content type.
*/
+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type;
@end
@interface GCDWebServerDataResponse (Extensions)
/**
* Creates a data response from text encoded using UTF-8.
*/
+ (instancetype)responseWithText:(NSString*)text;
/**
* Creates a data response from HTML encoded using UTF-8.
*/
+ (instancetype)responseWithHTML:(NSString*)html;
/**
* Creates a data response from an HTML template encoded using UTF-8.
* See -initWithHTMLTemplate:variables: for details.
*/
+ (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables;
/**
* Creates a data response from a serialized JSON object and the default
* "application/json" content type.
*/
+ (instancetype)responseWithJSONObject:(id)object;
/**
* Creates a data response from a serialized JSON object and a custom
* content type.
*/
+ (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type;
/**
* Initializes a data response from text encoded using UTF-8.
*/
- (instancetype)initWithText:(NSString*)text; // Encodes using UTF-8
- (instancetype)initWithHTML:(NSString*)html; // Encodes using UTF-8
- (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; // Simple template system that replaces all occurences of "%variable%" with corresponding value (encodes using UTF-8)
/**
* Initializes a data response from HTML encoded using UTF-8.
*/
- (instancetype)initWithHTML:(NSString*)html;
/**
* Initializes a data response from an HTML template encoded using UTF-8.
* All occurences of "%variable%" within the HTML template are replaced with
* their corresponding values.
*/
- (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables;
/**
* Initializes a data response from a serialized JSON object and the default
* "application/json" content type.
*/
- (instancetype)initWithJSONObject:(id)object;
/**
* Initializes a data response from a serialized JSON object and a custom
* content type.
*/
- (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type;
@end

View File

@@ -28,14 +28,54 @@
#import "GCDWebServerDataResponse.h"
#import "GCDWebServerHTTPStatusCodes.h"
// Returns responses with an HTML body containing the error message
/**
* The GCDWebServerDataResponse subclass of GCDWebServerDataResponse generates
* an HTML body from an HTTP status code and an error message.
*/
@interface GCDWebServerErrorResponse : GCDWebServerDataResponse
/**
* Creates a client error response with the corresponding HTTP status code.
*/
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
/**
* Creates a server error response with the corresponding HTTP status code.
*/
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
/**
* Creates a client error response with the corresponding HTTP status code
* and an optional underlying NSError.
*/
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4);
/**
* Creates a server error response with the corresponding HTTP status code
* and an optional underlying NSError.
*/
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4);
/**
* Initializes a client error response with the corresponding HTTP status code.
*/
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
/**
* Initializes a server error response with the corresponding HTTP status code.
*/
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
/**
* Initializes a client error response with the corresponding HTTP status code
* and an optional underlying NSError.
*/
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4);
/**
* Initializes a server error response with the corresponding HTTP status code
* and an optional underlying NSError.
*/
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4);
@end

View File

@@ -27,13 +27,67 @@
#import "GCDWebServerResponse.h"
/**
* The GCDWebServerFileResponse subclass of GCDWebServerResponse reads the body
* of the HTTP response from a file on disk.
*/
@interface GCDWebServerFileResponse : GCDWebServerResponse
/**
* Creates a response with the contents of a file and the content type
* automatically set from the file extension.
*/
+ (instancetype)responseWithFile:(NSString*)path;
/**
* Creates a response like +responseWithFile: but sets the "Content-Disposition"
* HTTP header appropriately for a download if the "attachment" argument is YES.
*/
+ (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment;
/**
* Creates a response like +responseWithFile: but restricts the file contents
* to a specific byte range.
*
* See -initWithFile:byteRange: for details.
*/
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range;
/**
* Creates a response like +responseWithFile:byteRange: but also sets the
* "Content-Disposition" HTTP header appropriately for a download if the
* "attachment" argument is YES.
*/
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
/**
* Initializes a response with the contents of a file and the content type
* automatically set from the file extension.
*/
- (instancetype)initWithFile:(NSString*)path;
/**
* Initializes a response like -initWithFile: but sets the "Content-Disposition"
* HTTP header appropriately for a download if the "attachment" argument is YES.
*/
- (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment;
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range; // Pass [NSNotFound, 0] to disable byte range entirely, [offset, length] to enable byte range from beginning of file or [NSNotFound, -bytes] from end of file
/**
* Initializes a response like -initWithFile: but restricts the file contents
* to a specific byte range. This range should be set to (NSNotFound, 0) for
* the full file, (offset, length) if expressed from the beginning of the file,
* or (NSNotFound, -length) if expressed from the end of the file. The "offset"
* and "length" values will be automatically adjusted to be compatible with the
* actual size of the file.
*
* This argument would typically be set to the value of the byteRange property
* of the current GCDWebServerRequest.
*/
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
@end

View File

@@ -112,12 +112,14 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
_size = (NSUInteger)info.st_size;
}
if (attachment) { // TODO: Use http://tools.ietf.org/html/rfc5987 to encode file names with special characters instead of using lossy conversion to ISO 8859-1
NSData* data = [[path lastPathComponent] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
NSString* fileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
if (fileName) {
[self setValue:[NSString stringWithFormat:@"attachment; filename=\"%@\"", fileName] forAdditionalHeader:@"Content-Disposition"];
ARC_RELEASE(fileName);
if (attachment) {
NSString* fileName = [path lastPathComponent];
NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
if (lossyFileName) {
NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)];
[self setValue:value forAdditionalHeader:@"Content-Disposition"];
ARC_RELEASE(lossyFileName);
} else {
DNOT_REACHED();
}

View File

@@ -25,11 +25,29 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "GCDWebServerStreamingResponse.h"
#import "GCDWebServerStreamedResponse.h"
typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
/**
* The GCDWebServerStreamingBlock is called to stream the data for the HTTP body.
* The block must return empty NSData when done or nil on error and set the
* "error" argument which is guaranteed to be non-NULL.
*/
typedef NSData* (^GCDWebServerStreamingBlock)(NSError** error);
/**
* The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams
* the body of the HTTP response using a GCD block.
*/
@interface GCDWebServerStreamedResponse : GCDWebServerResponse
/**
* Creates a response with streamed data and a given content type.
*/
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamingBlock)block;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamingBlock)block;
@interface GCDWebServerStreamingResponse : GCDWebServerResponse // Automatically enables chunked transfer encoding
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; // Block must return empty NSData when done or nil on error
@end

View File

@@ -27,19 +27,19 @@
#import "GCDWebServerPrivate.h"
@interface GCDWebServerStreamingResponse () {
@interface GCDWebServerStreamedResponse () {
@private
GCDWebServerStreamBlock _block;
GCDWebServerStreamingBlock _block;
}
@end
@implementation GCDWebServerStreamingResponse
@implementation GCDWebServerStreamedResponse
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamingBlock)block {
return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type streamBlock:block]);
}
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamingBlock)block {
if ((self = [super init])) {
_block = [block copy];

View File

@@ -138,7 +138,7 @@
</div>
<script type="text/x-tmpl" id="template-listing">
<tr class="row-file" data-path="{%=o.path%}" data-name="{%=o.name%}">
<tr class="row-file">
<td class="column-icon">
{% if (o.size != null) { %}
<button type="button" class="btn btn-default btn-xs button-download">

View File

@@ -25,9 +25,11 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var ENTER_KEYCODE = 13;
var _path = null;
var _reloading = false;
var _pendingReloads = [];
var _reloadingDisabled = 0;
function formatFileSize(bytes) {
if (bytes >= 1000000000) {
@@ -47,15 +49,27 @@ function _showError(message, textStatus, errorThrown) {
}));
}
function _disableReloads() {
_reloadingDisabled += 1;
}
function _enableReloads() {
_reloadingDisabled -= 1;
if (_pendingReloads.length > 0) {
_reload(_pendingReloads.shift());
}
}
function _reload(path) {
if (_reloading) {
if (_reloadingDisabled) {
if ($.inArray(path, _pendingReloads) < 0) {
_pendingReloads.push(path);
}
return;
}
_reloading = true;
_disableReloads();
$.ajax({
url: 'list',
type: 'GET',
@@ -64,6 +78,7 @@ function _reload(path) {
}).fail(function(jqXHR, textStatus, errorThrown) {
_showError("Failed retrieving contents of \"" + path + "\"", textStatus, errorThrown);
}).done(function(data, textStatus, jqXHR) {
var scrollPosition = $(document).scrollTop();
if (path != _path) {
$("#path").empty();
@@ -77,7 +92,7 @@ function _reload(path) {
$("#path").append('<li data-path="' + subpath + '"><a>' + components[i] + '</a></li>');
}
$("#path > li").click(function(event) {
_reload($(this).attr("data-path"));
_reload($(this).data("path"));
event.preventDefault();
});
$("#path").append('<li class="active">' + components[components.length - 1] + '</li>');
@@ -87,13 +102,13 @@ function _reload(path) {
$("#listing").empty();
for (var i = 0, file; file = data[i]; ++i) {
$("#listing").append(tmpl("template-listing", file));
$(tmpl("template-listing", file)).data(file).appendTo("#listing");
}
$(".edit").editable(function(value, settings) {
var name = $(this).parent().parent().attr("data-name");
var name = $(this).parent().parent().data("name");
if (value != name) {
var path = $(this).parent().parent().attr("data-path");
var path = $(this).parent().parent().data("path");
$.ajax({
url: 'move',
type: 'POST',
@@ -107,33 +122,42 @@ function _reload(path) {
}
return value;
}, {
onedit: function(settings, original) {
_disableReloads();
},
onsubmit: function(settings, original) {
_enableReloads();
},
onreset: function(settings, original) {
_enableReloads();
},
tooltip: 'Click to rename...'
});
$(".button-download").click(function(event) {
var path = $(this).parent().parent().attr("data-path");
var path = $(this).parent().parent().data("path");
setTimeout(function() {
window.location = "download?path=" + encodeURIComponent(path);
}, 0);
});
$(".button-open").click(function(event) {
var path = $(this).parent().parent().attr("data-path");
var path = $(this).parent().parent().data("path");
_reload(path);
});
$(".button-move").click(function(event) {
var path = $(this).parent().parent().attr("data-path");
var path = $(this).parent().parent().data("path");
if (path[path.length - 1] == "/") {
path = path.slice(0, path.length - 1);
}
$("#move-input").attr("data-path", path);
$("#move-input").data("path", path);
$("#move-input").val(path);
$("#move-modal").modal("show");
});
$(".button-delete").click(function(event) {
var path = $(this).parent().parent().attr("data-path");
var path = $(this).parent().parent().data("path");
$.ajax({
url: 'delete',
type: 'POST',
@@ -146,11 +170,9 @@ function _reload(path) {
});
});
$(document).scrollTop(scrollPosition);
}).always(function() {
_reloading = false;
if (_pendingReloads.length > 0) {
_reload(_pendingReloads.shift());
}
_enableReloads();
});
}
@@ -212,10 +234,16 @@ $(document).ready(function() {
});
$("#create-input").keypress(function(event) {
if (event.keyCode == ENTER_KEYCODE) {
$("#create-confirm").click();
};
});
$("#create-modal").on("shown.bs.modal", function(event) {
$("#create-input").focus();
$("#create-input").select();
})
});
$("#create-folder").click(function(event) {
$("#create-input").val("Untitled folder");
@@ -239,6 +267,12 @@ $(document).ready(function() {
}
});
$("#move-input").keypress(function(event) {
if (event.keyCode == ENTER_KEYCODE) {
$("#move-confirm").click();
};
});
$("#move-modal").on("shown.bs.modal", function(event) {
$("#move-input").focus();
$("#move-input").select();
@@ -246,7 +280,7 @@ $(document).ready(function() {
$("#move-confirm").click(function(event) {
$("#move-modal").modal("hide");
var oldPath = $("#move-input").attr("data-path");
var oldPath = $("#move-input").data("path");
var newPath = $("#move-input").val();
if ((newPath != "") && (newPath[0] == "/") && (newPath != oldPath)) {
$.ajax({

View File

@@ -29,31 +29,167 @@
@class GCDWebUploader;
@protocol GCDWebUploaderDelegate <NSObject>
/**
* Delegate methods for GCDWebUploaderDelegate.
*
* @warning These methods are always called on the main thread in a serialized way.
*/
@protocol GCDWebUploaderDelegate <GCDWebServerDelegate>
@optional
/**
* This method is called whenever a file has been downloaded.
*/
- (void)webUploader:(GCDWebUploader*)uploader didDownloadFileAtPath:(NSString*)path;
/**
* This method is called whenever a file has been uploaded.
*/
- (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path;
/**
* This method is called whenever a file or directory has been moved.
*/
- (void)webUploader:(GCDWebUploader*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
/**
* This method is called whenever a file or directory has been deleted.
*/
- (void)webUploader:(GCDWebUploader*)uploader didDeleteItemAtPath:(NSString*)path;
/**
* This method is called whenever a directory has been created.
*/
- (void)webUploader:(GCDWebUploader*)uploader didCreateDirectoryAtPath:(NSString*)path;
@end
/**
* The GCDWebUploader subclass of GCDWebServer implements an HTML 5 web browser
* interface for uploading or downloading files, and moving or deleting files
* or directories.
*
* See the README.md file for more information about the features of GCDWebUploader.
*/
@interface GCDWebUploader : GCDWebServer
/**
* Returns the upload directory as specified when the server was initialized.
*/
@property(nonatomic, readonly) NSString* uploadDirectory;
/**
* Sets the delegate for the server.
*/
@property(nonatomic, assign) id<GCDWebUploaderDelegate> delegate;
@property(nonatomic, copy) NSArray* allowedFileExtensions; // Default is nil i.e. all file extensions are allowed
@property(nonatomic) BOOL showHiddenFiles; // Default is NO
@property(nonatomic, copy) NSString* title; // Default is application name (must be HTML escaped)
@property(nonatomic, copy) NSString* header; // Default is same as title (must be HTML escaped)
@property(nonatomic, copy) NSString* prologue; // Default is mini help (must be raw HTML)
@property(nonatomic, copy) NSString* epilogue; // Default is nothing (must be raw HTML)
@property(nonatomic, copy) NSString* footer; // Default is application name and version (must be HTML escaped)
/**
* Restricts which files should be listed and allowed to be uploaded, downloaded,
* moved or deleted depending on their extensions.
*
* The default value is nil i.e. all file extensions are allowed.
*/
@property(nonatomic, copy) NSArray* allowedFileExtensions;
/**
* Sets if files and directories whose name start with a period should be
* listed and allowed to be uploaded, downloaded, moved or deleted.
*
* The default value is NO.
*/
@property(nonatomic) BOOL allowHiddenItems;
/**
* Sets the title for the uploader interface.
*
* The default value is the application name.
*
* @warning Any reserved HTML characters in the string value for this property
* must have been replaced by character entities e.g. "&" becomes "&amp;".
*/
@property(nonatomic, copy) NSString* title;
/**
* Sets the header for the uploader interface.
*
* The default value is the same as the title property.
*
* @warning Any reserved HTML characters in the string value for this property
* must have been replaced by character entities e.g. "&" becomes "&amp;".
*/
@property(nonatomic, copy) NSString* header;
/**
* Sets the prologue for the uploader interface.
*
* The default value is a short help text.
*
* @warning The string value for this property must be raw HTML
* e.g. "<p>Some text</p>"
*/
@property(nonatomic, copy) NSString* prologue;
/**
* Sets the epilogue for the uploader interface.
*
* The default value is nil i.e. no epilogue.
*
* @warning The string value for this property must be raw HTML
* e.g. "<p>Some text</p>"
*/
@property(nonatomic, copy) NSString* epilogue;
/**
* Sets the footer for the uploader interface.
*
* The default value is the application name and version.
*
* @warning Any reserved HTML characters in the string value for this property
* must have been replaced by character entities e.g. "&" becomes "&amp;".
*/
@property(nonatomic, copy) NSString* footer;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)initWithUploadDirectory:(NSString*)path;
@end
/**
* Hooks to customize the behavior of GCDWebUploader.
*
* @warning These methods can be called on any GCD thread.
*/
@interface GCDWebUploader (Subclassing)
- (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath; // Default implementation returns YES
- (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; // Default implementation returns YES
- (BOOL)shouldDeleteItemAtPath:(NSString*)path; // Default implementation returns YES
- (BOOL)shouldCreateDirectoryAtPath:(NSString*)path; // Default implementation returns YES
/**
* This method is called to check if a file is allowed to be uploaded.
* The uploaded file is available for inspection at "tempPath".
*
* The default implementation returns YES.
*/
- (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath;
/**
* This method is called to check if a file or directory is allowed to be moved.
*
* The default implementation returns YES.
*/
- (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
/**
* This method is called to check if a file or directory is allowed to be deleted.
*
* The default implementation returns YES.
*/
- (BOOL)shouldDeleteItemAtPath:(NSString*)path;
/**
* This method is called to check if a directory is allowed to be created.
*
* The default implementation returns YES.
*/
- (BOOL)shouldCreateDirectoryAtPath:(NSString*)path;
@end

View File

@@ -45,9 +45,8 @@
@interface GCDWebUploader () {
@private
NSString* _uploadDirectory;
id<GCDWebUploaderDelegate> __unsafe_unretained _delegate;
NSArray* _allowedExtensions;
BOOL _showHidden;
BOOL _allowHidden;
NSString* _title;
NSString* _header;
NSString* _prologue;
@@ -95,7 +94,7 @@
}
NSString* directoryName = [absolutePath lastPathComponent];
if (!_showHidden && [directoryName hasPrefix:@"."]) {
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Listing directory name \"%@\" is not allowed", directoryName];
}
@@ -107,7 +106,7 @@
NSMutableArray* array = [NSMutableArray array];
for (NSString* item in [contents sortedArrayUsingSelector:@selector(localizedStandardCompare:)]) {
if (_showHidden || ![item hasPrefix:@"."]) {
if (_allowHidden || ![item hasPrefix:@"."]) {
NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[absolutePath stringByAppendingPathComponent:item] error:NULL];
NSString* type = [attributes objectForKey:NSFileType];
if ([type isEqualToString:NSFileTypeRegular] && [self _checkFileExtension:item]) {
@@ -139,13 +138,13 @@
}
NSString* fileName = [absolutePath lastPathComponent];
if (([fileName hasPrefix:@"."] && !_showHidden) || ![self _checkFileExtension:fileName]) {
if (([fileName hasPrefix:@"."] && !_allowHidden) || ![self _checkFileExtension:fileName]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading file name \"%@\" is not allowed", fileName];
}
if ([_delegate respondsToSelector:@selector(webUploader:didDownloadFileAtPath: )]) {
if ([self.delegate respondsToSelector:@selector(webUploader:didDownloadFileAtPath: )]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didDownloadFileAtPath:absolutePath];
[self.delegate webUploader:self didDownloadFileAtPath:absolutePath];
});
}
return [GCDWebServerFileResponse responseWithFile:absolutePath isAttachment:YES];
@@ -156,7 +155,7 @@
NSString* contentType = (range.location != NSNotFound ? @"application/json" : @"text/plain; charset=utf-8"); // Required when using iFrame transport (see https://github.com/blueimp/jQuery-File-Upload/wiki/Setup)
GCDWebServerMultiPartFile* file = [request.files objectForKey:@"files[]"];
if ((!_showHidden && [file.fileName hasPrefix:@"."]) || ![self _checkFileExtension:file.fileName]) {
if ((!_allowHidden && [file.fileName hasPrefix:@"."]) || ![self _checkFileExtension:file.fileName]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName];
}
NSString* relativePath = [(GCDWebServerMultiPartArgument*)[request.arguments objectForKey:@"path"] string];
@@ -171,9 +170,9 @@
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath];
}
if ([_delegate respondsToSelector:@selector(webUploader:didUploadFileAtPath:)]) {
if ([self.delegate respondsToSelector:@selector(webUploader:didUploadFileAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didUploadFileAtPath:absolutePath];
[self.delegate webUploader:self didUploadFileAtPath:absolutePath];
});
}
return [GCDWebServerDataResponse responseWithJSONObject:@{} contentType:contentType];
@@ -191,7 +190,7 @@
NSString* newAbsolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:newRelativePath]];
NSString* itemName = [newAbsolutePath lastPathComponent];
if ((!_showHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving to item name \"%@\" is not allowed", itemName];
}
@@ -204,9 +203,9 @@
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving \"%@\" to \"%@\"", oldRelativePath, newRelativePath];
}
if ([_delegate respondsToSelector:@selector(webUploader:didMoveItemFromPath:toPath:)]) {
if ([self.delegate respondsToSelector:@selector(webUploader:didMoveItemFromPath:toPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath];
[self.delegate webUploader:self didMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath];
});
}
return [GCDWebServerDataResponse responseWithJSONObject:@{}];
@@ -221,7 +220,7 @@
}
NSString* itemName = [absolutePath lastPathComponent];
if (([itemName hasPrefix:@"."] && !_showHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName];
}
@@ -234,9 +233,9 @@
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath];
}
if ([_delegate respondsToSelector:@selector(webUploader:didDeleteItemAtPath:)]) {
if ([self.delegate respondsToSelector:@selector(webUploader:didDeleteItemAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didDeleteItemAtPath:absolutePath];
[self.delegate webUploader:self didDeleteItemAtPath:absolutePath];
});
}
return [GCDWebServerDataResponse responseWithJSONObject:@{}];
@@ -247,7 +246,7 @@
NSString* absolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:relativePath]];
NSString* directoryName = [absolutePath lastPathComponent];
if (!_showHidden && [directoryName hasPrefix:@"."]) {
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName];
}
@@ -260,9 +259,9 @@
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath];
}
if ([_delegate respondsToSelector:@selector(webUploader:didCreateDirectoryAtPath:)]) {
if ([self.delegate respondsToSelector:@selector(webUploader:didCreateDirectoryAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didCreateDirectoryAtPath:absolutePath];
[self.delegate webUploader:self didCreateDirectoryAtPath:absolutePath];
});
}
return [GCDWebServerDataResponse responseWithJSONObject:@{}];
@@ -272,7 +271,7 @@
@implementation GCDWebUploader
@synthesize uploadDirectory=_uploadDirectory, delegate=_delegate, allowedFileExtensions=_allowedExtensions, showHiddenFiles=_showHidden,
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden,
title=_title, header=_header, prologue=_prologue, epilogue=_epilogue, footer=_footer;
- (instancetype)initWithUploadDirectory:(NSString*)path {

View File

@@ -33,12 +33,16 @@
#import "GCDWebServerURLEncodedFormRequest.h"
#import "GCDWebServerDataResponse.h"
#import "GCDWebServerStreamingResponse.h"
#import "GCDWebServerStreamedResponse.h"
#import "GCDWebDAVServer.h"
#import "GCDWebUploader.h"
#ifndef __GCDWEBSERVER_ENABLE_TESTING__
#error __GCDWEBSERVER_ENABLE_TESTING__ must be defined
#endif
typedef enum {
kMode_WebServer = 0,
kMode_HTMLPage,
@@ -48,6 +52,81 @@ typedef enum {
kMode_StreamingResponse
} Mode;
@interface Delegate : NSObject <GCDWebServerDelegate, GCDWebDAVServerDelegate, GCDWebUploaderDelegate>
@end
@implementation Delegate
- (void)_logDelegateCall:(SEL)selector {
fprintf(stdout, "<DELEGATE METHOD \"%s\" CALLED>\n", [NSStringFromSelector(selector) UTF8String]);
}
- (void)webServerDidStart:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)webServerDidConnect:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)webServerDidDisconnect:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)webServerDidStop:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didDownloadFileAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didUploadFileAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didDeleteItemAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didCreateDirectoryAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didDownloadFileAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didDeleteItemAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didCreateDirectoryAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
@end
int main(int argc, const char* argv[]) {
int result = -1;
@autoreleasepool {
@@ -55,9 +134,13 @@ int main(int argc, const char* argv[]) {
BOOL recording = NO;
NSString* rootDirectory = NSHomeDirectory();
NSString* testDirectory = nil;
NSString* authenticationMethod = nil;
NSString* authenticationRealm = nil;
NSString* authenticationUser = nil;
NSString* authenticationPassword = nil;
if (argc == 1) {
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | webDAV | webUploader | streamingResponse] [-record] [-root directory] [-tests directory]\n\n", basename((char*)argv[0]));
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | webDAV | webUploader | streamingResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password]\n\n", basename((char*)argv[0]));
} else {
for (int i = 1; i < argc; ++i) {
if (argv[i][0] != '-') {
@@ -86,6 +169,18 @@ int main(int argc, const char* argv[]) {
} else if (!strcmp(argv[i], "-tests") && (i + 1 < argc)) {
++i;
testDirectory = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:argv[i] length:strlen(argv[i])] stringByStandardizingPath];
} else if (!strcmp(argv[i], "-authenticationMethod") && (i + 1 < argc)) {
++i;
authenticationMethod = [NSString stringWithUTF8String:argv[i]];
} else if (!strcmp(argv[i], "-authenticationRealm") && (i + 1 < argc)) {
++i;
authenticationRealm = [NSString stringWithUTF8String:argv[i]];
} else if (!strcmp(argv[i], "-authenticationUser") && (i + 1 < argc)) {
++i;
authenticationUser = [NSString stringWithUTF8String:argv[i]];
} else if (!strcmp(argv[i], "-authenticationPassword") && (i + 1 < argc)) {
++i;
authenticationPassword = [NSString stringWithUTF8String:argv[i]];
}
}
}
@@ -172,7 +267,7 @@ int main(int argc, const char* argv[]) {
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
__block int countDown = 10;
return [GCDWebServerStreamingResponse responseWithContentType:@"text/plain" streamBlock:^NSData *(NSError** error) {
return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" streamBlock:^NSData *(NSError** error) {
usleep(100 * 1000);
if (countDown) {
@@ -195,21 +290,36 @@ int main(int argc, const char* argv[]) {
#endif
if (webServer) {
Delegate* delegate = [[Delegate alloc] init];
webServer.delegate = delegate;
if (testDirectory) {
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
result = (int)[webServer runTestsInDirectory:testDirectory withPort:8080];
result = (int)[webServer runTestsWithOptions:@{GCDWebServerOption_Port: @8080} inDirectory:testDirectory];
} else {
if (recording) {
fprintf(stdout, "<RECORDING ENABLED>\n");
webServer.recordingEnabled = YES;
}
fprintf(stdout, "\n");
if ([webServer runWithPort:8080]) {
NSMutableDictionary* options = [NSMutableDictionary dictionary];
[options setObject:@8080 forKey:GCDWebServerOption_Port];
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
if (authenticationUser && authenticationPassword) {
[options setValue:authenticationRealm forKey:GCDWebServerOption_AuthenticationRealm];
[options setObject:@{authenticationUser: authenticationPassword} forKey:GCDWebServerOption_AuthenticationAccounts];
if ([authenticationMethod isEqualToString:@"Basic"]) {
[options setObject:GCDWebServerAuthenticationMethod_Basic forKey:GCDWebServerOption_AuthenticationMethod];
} else if ([authenticationMethod isEqualToString:@"Digest"]) {
[options setObject:GCDWebServerAuthenticationMethod_DigestAccess forKey:GCDWebServerOption_AuthenticationMethod];
}
}
if ([webServer runWithOptions:options]) {
result = 0;
}
}
#if !__has_feature(objc_arc)
[webServer release];
[delegate release];
#endif
}
}

137
README.md
View File

@@ -1,10 +1,12 @@
Overview
========
GCDWebServer is a lightweight GCD based HTTP 1.1 server designed to be embedded in Mac & iOS apps. It was written from scratch with the following goals in mind:
* Easy to use and understand architecture with only 4 core classes: server, connection, request and response
* Well designed API for easy integration and customization
* Entirely built with an event-driven design using [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) for maximal performance and concurrency
[![Build Status](https://travis-ci.org/swisspol/GCDWebServer.svg?branch=master)](https://travis-ci.org/swisspol/GCDWebServer)
GCDWebServer is a modern and lightweight GCD based HTTP 1.1 server designed to be embedded in OS X & iOS apps. It was written from scratch with the following goals in mind:
* Elegant and easy to use architecture with only 4 core classes: server, connection, request and response (see "Understanding GCDWebServer's Architecture" below)
* Well designed API with fully documented headers for easy integration and customization
* Entirely built with an event-driven design using [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) for best performance and concurrency
* No dependencies on third-party source code
* Available under a friendly [New BSD License](LICENSE)
@@ -15,14 +17,12 @@ Extra built-in features:
* [Chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding) for request and response HTTP bodies
* [HTTP compression](https://en.wikipedia.org/wiki/HTTP_compression) with gzip for request and response HTTP bodies
* [HTTP range](https://en.wikipedia.org/wiki/Byte_serving) support for requests of local files
* [Basic](https://en.wikipedia.org/wiki/Basic_access_authentication) and [Digest Access](https://en.wikipedia.org/wiki/Digest_access_authentication) authentications for password protection
* Automatically handle transitions between foreground, background and suspended modes in iOS apps
Included extensions:
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of GCDWebServer that implements an interface for uploading and downloading files from an iOS app's sandbox using a web browser
* [GCDWebDAVServer](GCDWebDAVServer/GCDWebDAVServer.h): subclass of GCDWebServer that implements a class 1 [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server (with partial class 2 support for OS X Finder)
What's not available out of the box but can be implemented on top of the API:
* Authentication like [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
* Web forms submitted using "multipart/mixed"
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of ```GCDWebServer``` that implements an interface for uploading and downloading files using a web browser
* [GCDWebDAVServer](GCDWebDAVServer/GCDWebDAVServer.h): subclass of ```GCDWebServer``` that implements a class 1 [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server (with partial class 2 support for OS X Finder)
What's not supported (but not really required from an embedded HTTP server):
* Keep-alive connections
@@ -35,7 +35,7 @@ Requirements:
Getting Started
===============
Download or checkout the source for GCDWebServer then add the entire "GCDWebServer" subfolder to your Xcode project. If you intend to use one of the extensions like GCDWebDAVServer or GCDWebUploader, add these subfolders as well.
Download or check out the [latest release](https://github.com/swisspol/GCDWebServer/releases) of GCDWebServer then add the entire "GCDWebServer" subfolder to your Xcode project. If you intend to use one of the extensions like GCDWebDAVServer or GCDWebUploader, add these subfolders as well.
Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Xcode project's Podfile:
```
@@ -53,10 +53,12 @@ pod "GCDWebServer/WebDAV", "~> 2.0"
Hello World
===========
This code snippet shows how to implement a custom HTTP server that runs on port 8080 and returns a "Hello World" HTML page to any request &mdash; since GCDWebServer uses GCD blocks to handle requests, no subclassing or delegates are needed:
These code snippets show how to implement a custom HTTP server that runs on port 8080 and returns a "Hello World" HTML page to any request. Since GCDWebServer uses GCD blocks to handle requests, no subclassing or delegates are needed, which results in very clean code.
**OS X version (command line tool):**
```objectivec
#import "GCDWebServer.h"
#import "GCDWebServerDataResponse.h"
int main(int argc, const char* argv[]) {
@autoreleasepool {
@@ -64,7 +66,7 @@ int main(int argc, const char* argv[]) {
// Create server
GCDWebServer* webServer = [[GCDWebServer alloc] init];
// Add a handler to respond to requests on any URL
// Add a handler to respond to GET requests on any URL
[webServer addDefaultHandlerForMethod:@"GET"
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
@@ -73,32 +75,59 @@ int main(int argc, const char* argv[]) {
}];
// Use convenience method that runs server on port 8080 until SIGINT received
[webServer runWithPort:8080];
// Destroy server
[webServer release];
// Use convenience method that runs server on port 8080 until SIGINT received (i.e. Ctrl-C in Terminal)
[webServer runWithPort:8080 bonjourName:nil];
}
return 0;
}
```
**iOS version:**
```objectivec
#import "GCDWebServer.h"
#import "GCDWebServerDataResponse.h"
static GCDWebServer* _webServer = nil; // This should really be an ivar of your application's delegate class
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
// Create server
_webServer = [[GCDWebServer alloc] init];
// Add a handler to respond to GET requests on any URL
[_webServer addDefaultHandlerForMethod:@"GET"
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
return [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
}];
// Start server on port 8080
[_webServer startWithPort:8080 bonjourName:nil];
return YES;
}
```
Web Based Uploads in iOS Apps
=============================
GCDWebUploader is a subclass of GCDWebServer that provides a ready-to-use HTML 5 file uploader & downloader. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using a clean user interface in their web browser.
GCDWebUploader is a subclass of ```GCDWebServer``` that provides a ready-to-use HTML 5 file uploader & downloader. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using a clean user interface in their web browser.
Simply instantiate and run a GCDWebUploader instance then visit http://{YOUR-IOS-DEVICE-IP-ADDRESS}/ from your web browser:
Simply instantiate and run a ```GCDWebUploader``` instance then visit ```http://{YOUR-IOS-DEVICE-IP-ADDRESS}/``` from your web browser:
```objectivec
#import "GCDWebUploader.h"
static GCDWebUploader* _webUploader = nil; // This should really be an ivar of your application's delegate class
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
GCDWebUploader* webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
[webUploader start];
NSLog(@"Visit %@ in your web browser", webUploader.serverURL);
_webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
[_webUploader start];
NSLog(@"Visit %@ in your web browser", _webUploader.serverURL);
return YES;
}
```
@@ -106,20 +135,22 @@ Simply instantiate and run a GCDWebUploader instance then visit http://{YOUR-IOS
WebDAV Server in iOS Apps
=========================
GCDWebDAVServer is a subclass of GCDWebServer that provides a class 1 compliant [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using any WebDAV client like [Transmit](https://panic.com/transmit/) (Mac), [ForkLift](http://binarynights.com/forklift/) (Mac) or [CyberDuck](http://cyberduck.io/) (Mac / Windows).
GCDWebDAVServer is a subclass of ```GCDWebServer``` that provides a class 1 compliant [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using any WebDAV client like [Transmit](https://panic.com/transmit/) (Mac), [ForkLift](http://binarynights.com/forklift/) (Mac) or [CyberDuck](http://cyberduck.io/) (Mac / Windows).
GCDWebDAVServer should also work with the [OS X Finder](http://support.apple.com/kb/PH13859) as it is partially class 2 compliant (but only when the client is the OS X WebDAV implementation).
Simply instantiate and run a GCDWebDAVServer instance then connect to http://{YOUR-IOS-DEVICE-IP-ADDRESS}/ using a WebDAV client:
Simply instantiate and run a ```GCDWebDAVServer``` instance then connect to ```http://{YOUR-IOS-DEVICE-IP-ADDRESS}/``` using a WebDAV client:
```objectivec
#import "GCDWebDAVServer.h"
static GCDWebDAVServer* _davServer = nil; // This should really be an ivar of your application's delegate class
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
GCDWebDAVServer* davServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:documentsPath];
[davServer start];
NSLog(@"Visit %@ in your WebDAV client", davServer.serverURL);
_davServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:documentsPath];
[_davServer start];
NSLog(@"Visit %@ in your WebDAV client", _davServer.serverURL);
return YES;
}
```
@@ -127,7 +158,7 @@ Simply instantiate and run a GCDWebDAVServer instance then connect to http://{YO
Serving a Static Website
========================
GCDWebServer includes a built-in handler that can recursively serve a directory (it also lets you control how the "Cache-Control" header should be set):
GCDWebServer includes a built-in handler that can recursively serve a directory (it also lets you control how the ["Cache-Control"](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) header should be set):
```objectivec
#import "GCDWebServer.h"
@@ -138,7 +169,7 @@ int main(int argc, const char* argv[]) {
GCDWebServer* webServer = [[GCDWebServer alloc] init];
[webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:3600 allowRangeRequests:YES];
[webServer runWithPort:8080];
[webServer release];
[webServer release]; // Remove if using ARC
}
return 0;
@@ -148,20 +179,20 @@ int main(int argc, const char* argv[]) {
Using GCDWebServer
==================
You start by creating an instance of the 'GCDWebServer' class. Note that you can have multiple web servers running in the same app as long as they listen on different ports.
You start by creating an instance of the ```GCDWebServer``` class. Note that you can have multiple web servers running in the same app as long as they listen on different ports.
Then you add one or more "handlers" to the server: each handler gets a chance to handle an incoming web request and provide a response. Handlers are called in a LIFO queue, so the latest added handler overrides any previously added ones.
Finally you start the server on a given port.
Understanding GCDWebServer Architecture
=======================================
Understanding GCDWebServer's Architecture
=========================================
GCDWebServer is made of only 4 core classes:
* 'GCDWebServer' manages the socket that listens for new HTTP connections and the list of handlers used by the server.
* 'GCDWebServerConnection' is instantiated by 'GCDWebServer' to handle each new HTTP connection. Each instance stays alive until the connection is closed. You cannot use this class directly, but it is exposed so you can subclass it to override some hooks.
* 'GCDWebServerRequest' is created by the 'GCDWebServerConnection' instance after HTTP headers have been received. It wraps the request and handles the HTTP body if any. GCDWebServer comes with several subclasses of 'GCDWebServerRequest' to handle common cases like storing the body in memory or stream it to a file on disk. See [GCDWebServerRequest.h](CGDWebServer/GCDWebServerRequest.h) for the full list.
* 'GCDWebServerResponse' is created by the request handler and wraps the response HTTP headers and optional body. GCDWebServer provides several subclasses of 'GCDWebServerResponse' to handle common cases like HTML text in memory or streaming a file from disk. See [GCDWebServerResponse.h](CGDWebServer/GCDWebServerResponse.h) for the full list.
GCDWebServer's architecture consists of only 4 core classes:
* [GCDWebServer](GCDWebServer/Core/GCDWebServer.h) manages the socket that listens for new HTTP connections and the list of handlers used by the server.
* [GCDWebServerConnection](GCDWebServer/Core/GCDWebServerConnection.h) is instantiated by ```GCDWebServer``` to handle each new HTTP connection. Each instance stays alive until the connection is closed. You cannot use this class directly, but it is exposed so you can subclass it to override some hooks.
* [GCDWebServerRequest](GCDWebServer/Core/GCDWebServerRequest.h) is created by the ```GCDWebServerConnection``` instance after HTTP headers have been received. It wraps the request and handles the HTTP body if any. GCDWebServer comes with [several subclasses](GCDWebServer/Requests) of ```GCDWebServerRequest``` to handle common cases like storing the body in memory or stream it to a file on disk.
* [GCDWebServerResponse](GCDWebServer/Core/GCDWebServerResponse.h) is created by the request handler and wraps the response HTTP headers and optional body. GCDWebServer comes with [several subclasses](GCDWebServer/Responses) of ```GCDWebServerResponse``` to handle common cases like HTML text in memory or streaming a file from disk.
Implementing Handlers
=====================
@@ -169,15 +200,29 @@ Implementing Handlers
GCDWebServer relies on "handlers" to process incoming web requests and generating responses. Handlers are implemented with GCD blocks which makes it very easy to provide your owns. However, they are executed on arbitrary threads within GCD so __special attention must be paid to thread-safety and re-entrancy__.
Handlers require 2 GCD blocks:
* The 'GCDWebServerMatchBlock' is called on every handler added to the 'GCDWebServer' instance whenever a web request has started (i.e. HTTP headers have been received). It is passed the basic info for the web request (HTTP method, URL, headers...) and must decide if it wants to handle it or not. If yes, it must return a 'GCDWebServerRequest' instance (see above). Otherwise, it simply returns nil.
* The 'GCDWebServerProcessBlock' is called after the web request has been fully received and is passed the 'GCDWebServerRequest' instance created at the previous step. It must return a 'GCDWebServerResponse' instance (see above) or nil on error.
* The ```GCDWebServerMatchBlock``` is called on every handler added to the ```GCDWebServer``` instance whenever a web request has started (i.e. HTTP headers have been received). It is passed the basic info for the web request (HTTP method, URL, headers...) and must decide if it wants to handle it or not. If yes, it must return a new ```GCDWebServerRequest``` instance (see above) created with this info. Otherwise, it simply returns nil.
* The ```GCDWebServerProcessBlock``` is called after the web request has been fully received and is passed the ```GCDWebServerRequest``` instance created at the previous step. It must return a ```GCDWebServerResponse``` instance (see above) or nil on error, which will result in a 500 HTTP status code returned to the client. It's however recommended to return an instance of [GCDWebServerErrorResponse](GCDWebServer/Responses/GCDWebServerErrorResponse.h) on error so more useful information can be returned to the client.
Note that most methods on 'GCDWebServer' to add handlers only require the 'GCDWebServerProcessBlock' as they already provide a built-in 'GCDWebServerMatchBlock' e.g. to match a URL path with a Regex.
Note that most methods on ```GCDWebServer``` to add handlers only require the ```GCDWebServerProcessBlock``` as they already provide a built-in ```GCDWebServerMatchBlock``` e.g. to match a URL path with a Regex.
GCDWebServer & Background Mode for iOS Apps
===========================================
When doing networking operations in iOS apps, you must handle carefully [what happens when iOS puts the app in the background](https://developer.apple.com/library/ios/technotes/tn2277/_index.html). Typically you must stop any network servers while the app is in the background and restart them when the app comes back to the foreground. This can become quite complex considering servers might have ongoing connections when they need to be stopped.
Fortunately, GCDWebServer does all of this automatically for you:
- GCDWebServer begins a [background task](https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html) whenever the first HTTP connection is opened and ends it only when the last one is closed. This prevents iOS from suspending the app after it goes in the background, which would immediately kill HTTP connections to the client.
- While the app is in the background, as long as new HTTP connections keep being initiated, the background task will continue to exist and iOS will not suspend the app (unless under sudden and unexpected memory pressure).
- If the app is still in the background when the last HTTP connection is closed, GCDWebServer will suspend itself and stop accepting new connections as if you had called ```-stop``` (this behavior can be disabled with the ```GCDWebServerOption_AutomaticallySuspendInBackground``` option).
- If the app goes in the background while no HTTP connections are opened, GCDWebServer will immediately suspend itself and stop accepting new connections as if you had called ```-stop``` (this behavior can be disabled with the ```GCDWebServerOption_AutomaticallySuspendInBackground``` option).
- If the app comes back to the foreground and GCDWebServer had been suspended, it will automatically resume itself and start accepting again new HTTP connections as if you had called ```-start```.
HTTP connections are often initiated in batches (or bursts), for instance when loading a web page with multiple resources. This makes it difficult to accurately detect when the *very last* HTTP connection has been closed: it's possible 2 consecutive HTTP connections part of the same batch would be separated by a small delay instead of overlapping. It would be bad for the client if GCDWebServer suspended itself right in between. The ```GCDWebServerOption_ConnectedStateCoalescingInterval``` option solves this problem elegantly by forcing GCDWebServer to wait some extra delay before performing any action after the last HTTP connection has been closed, just in case a new HTTP connection is initiated within this delay.
Advanced Example 1: Implementing HTTP Redirects
===============================================
Here's an example handler that redirects "/" to "/index.html" using the convenience method on 'GCDWebServerResponse' (it sets the HTTP status code and 'Location' header automatically):
Here's an example handler that redirects "/" to "/index.html" using the convenience method on ```GCDWebServerResponse``` (it sets the HTTP status code and "Location" header automatically):
```objectivec
[self addHandlerForMethod:@"GET"
@@ -195,8 +240,8 @@ Advanced Example 2: Implementing Forms
======================================
To implement an HTTP form, you need a pair of handlers:
* The GET handler does not expect any body in the HTTP request and therefore uses the 'GCDWebServerRequest' class. The handler generates a response containing a simple HTML form.
* The POST handler expects the form values to be in the body of the HTTP request and percent-encoded. Fortunately, GCDWebServer provides the request class 'GCDWebServerURLEncodedFormRequest' which can automatically parse such bodies. The handler simply echoes back the value from the user submitted form.
* The GET handler does not expect any body in the HTTP request and therefore uses the ```GCDWebServerRequest``` class. The handler generates a response containing a simple HTML form.
* The POST handler expects the form values to be in the body of the HTTP request and percent-encoded. Fortunately, GCDWebServer provides the request class ```GCDWebServerURLEncodedFormRequest``` which can automatically parse such bodies. The handler simply echoes back the value from the user submitted form.
```objectivec
[webServer addHandlerForMethod:@"GET"
@@ -231,7 +276,7 @@ To implement an HTTP form, you need a pair of handlers:
Advanced Example 3: Serving a Dynamic Website
=============================================
GCDWebServer provides an extension to the 'GCDWebServerDataResponse' class that can return HTML content generated from a template and a set of variables (using the format '%variable%'). It is a very basic template system and is really intended as a starting point to building more advanced template systems by subclassing 'GCDWebServerResponse'.
GCDWebServer provides an extension to the ```GCDWebServerDataResponse``` class that can return HTML content generated from a template and a set of variables (using the format ```%variable%```). It is a very basic template system and is really intended as a starting point to building more advanced template systems by subclassing ```GCDWebServerResponse```.
Assuming you have a website directory in your app containing HTML template files along with the corresponding CSS, scripts and images, it's pretty easy to turn it into a dynamic website:
@@ -272,4 +317,4 @@ Final Example: File Downloads and Uploads From iOS App
GCDWebServer was originally written for the [ComicFlow](http://itunes.apple.com/us/app/comicflow/id409290355?mt=8) comic reader app for iPad. It allow users to connect to their iPad with their web browser over WiFi and then upload, download and organize comic files inside the app.
ComicFlow is [entirely open-source](https://github.com/swisspol/ComicFlow) and you can see how it uses GCDWebUploader in the [WebServer.m](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.m) file.
ComicFlow is [entirely open-source](https://github.com/swisspol/ComicFlow) and you can see how it uses GCDWebServer in the [WebServer.h](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.h) and [WebServer.m](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.m) files.

View File

@@ -1,6 +1,14 @@
#!/bin/sh -ex
#!/bin/bash -ex
TARGET="GCDWebServer (Mac)"
OSX_SDK="macosx"
if [ -z "$TRAVIS" ]; then
IOS_SDK="iphoneos"
else
IOS_SDK="iphonesimulator"
fi
OSX_TARGET="GCDWebServer (Mac)"
IOS_TARGET="GCDWebServer (iOS)"
CONFIGURATION="Release"
MRC_BUILD_DIR="/tmp/GCDWebServer-MRC"
@@ -14,17 +22,25 @@ PAYLOAD_DIR="/tmp/GCDWebServer"
function runTests {
rm -rf "$PAYLOAD_DIR"
ditto -x -k "$PAYLOAD_ZIP" "$PAYLOAD_DIR"
find "$PAYLOAD_DIR" -type d -exec SetFile -d "1/1/2014" -m "1/1/2014" '{}' \; # ZIP archives do not preserve directories dates
TZ=GMT find "$PAYLOAD_DIR" -type d -exec SetFile -d "1/1/2014 00:00:00" -m "1/1/2014 00:00:00" '{}' \; # ZIP archives do not preserve directories dates
logLevel=2 $1 -mode "$2" -root "$PAYLOAD_DIR/Payload" -tests "$3"
}
# Build in manual memory management mode
rm -rf "MRC_BUILD_DIR"
xcodebuild -target "$TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
# Build for iOS in manual memory management mode (TODO: run tests on iOS)
rm -rf "$MRC_BUILD_DIR"
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
# Build in ARC mode
rm -rf "ARC_BUILD_DIR"
xcodebuild -target "$TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
# Build for iOS in ARC mode (TODO: run tests on iOS)
rm -rf "$ARC_BUILD_DIR"
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
# Build for OS X in manual memory management mode
rm -rf "$MRC_BUILD_DIR"
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
# Build for OS X in ARC mode
rm -rf "$ARC_BUILD_DIR"
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
# Run tests
runTests $MRC_PRODUCT "webServer" "Tests/WebServer"

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:52:42 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:52:47 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:52:47 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/capable_green_ipad_l.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/hero_mba_11.jpg</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:51:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:51:14 GMT</D:getlastmodified><D:getcontentlength>106799</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,9 +7,9 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:52:51 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy%20(4:11:14,%209:52%20PM).txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:07 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/capable_green_ipad_l.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/hero_mba_11.jpg</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:51:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:51:14 GMT</D:getlastmodified><D:getcontentlength>106799</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:07 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy%20(4:11:14,%209:52%20PM).txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:13 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Test.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:14 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:14 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Test.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:22 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Test%20File.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-12T04:53:22+00:00</D:creationdate><D:getlastmodified>Sat, 12 Apr 2014 04:53:22 GMT</D:getlastmodified><D:getcontentlength>21</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,9 +7,9 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:35 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Test%20File.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-12T04:53:22+00:00</D:creationdate><D:getlastmodified>Sat, 12 Apr 2014 04:53:22 GMT</D:getlastmodified><D:getcontentlength>21</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Text%20Files</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T04:53:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:39 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Text%20Files</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T04:53:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:44 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Test.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:44 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Text%20Files</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T04:53:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:56 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/capable_green_ipad_l.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/hero_mba_11.jpg</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:51:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:51:14 GMT</D:getlastmodified><D:getcontentlength>106799</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:56 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:56 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:56 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/capable_green_ipad_l.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/hero_mba_11.jpg</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:51:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:51:14 GMT</D:getlastmodified><D:getcontentlength>106799</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:13 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/Green%20iPad.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/hero_mba_11.jpg</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:51:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:51:14 GMT</D:getlastmodified><D:getcontentlength>106799</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:13 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/Green%20iPad.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/hero_mba_11.jpg</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:51:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:51:14 GMT</D:getlastmodified><D:getcontentlength>106799</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:13 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/Green%20iPad.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/hero_mba_11.jpg</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:51:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:51:14 GMT</D:getlastmodified><D:getcontentlength>106799</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:14 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:16 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/Green%20iPad.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:16 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/Green%20iPad.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:16 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:17 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/Green%20iPad.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:18 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/Green%20iPad.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Test%20File.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-12T05:11:26+00:00</D:creationdate><D:getlastmodified>Sat, 12 Apr 2014 05:11:26 GMT</D:getlastmodified><D:getcontentlength>21</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Test%20File.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-12T05:11:26+00:00</D:creationdate><D:getlastmodified>Sat, 12 Apr 2014 05:11:26 GMT</D:getlastmodified><D:getcontentlength>21</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Test%20File.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-12T05:11:26+00:00</D:creationdate><D:getlastmodified>Sat, 12 Apr 2014 05:11:26 GMT</D:getlastmodified><D:getcontentlength>21</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Test%20File.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-12T05:11:26+00:00</D:creationdate><D:getlastmodified>Sat, 12 Apr 2014 05:11:26 GMT</D:getlastmodified><D:getcontentlength>21</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images/Green%20iPad.png</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T21:46:56+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 21:46:56 GMT</D:getlastmodified><D:getcontentlength>116066</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/Test%20File.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-12T05:11:26+00:00</D:creationdate><D:getlastmodified>Sat, 12 Apr 2014 05:11:26 GMT</D:getlastmodified><D:getcontentlength>21</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/PDF%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:31 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Apple%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Apple%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:32 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/Apple%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Apple%20Reports/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Apple%20Reports/Apple%20Economic%20Impact%20on%20Cupertino.pdf</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2013-05-01T12:01:13+00:00</D:creationdate><D:getlastmodified>Wed, 01 May 2013 12:01:13 GMT</D:getlastmodified><D:getcontentlength>181952</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Apple%20Reports/Test%20File.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-12T05:11:26+00:00</D:creationdate><D:getlastmodified>Sat, 12 Apr 2014 05:11:26 GMT</D:getlastmodified><D:getcontentlength>21</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:32 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:">
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Apple%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Apple%20Reports</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/Copy.txt</D:href><D:propstat><D:prop><D:resourcetype/><D:creationdate>2014-04-10T11:10:14+00:00</D:creationdate><D:getlastmodified>Thu, 10 Apr 2014 11:10:14 GMT</D:getlastmodified><D:getcontentlength>271</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T09:01:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
<D:response><D:href>/images</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-01-01T00:00:00+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus>

Some files were not shown because too many files have changed in this diff Show More