59 Commits
2.1 ... 2.3

Author SHA1 Message Date
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
113 changed files with 1205 additions and 656 deletions

View File

@@ -29,14 +29,15 @@
@class GCDWebDAVServer; @class GCDWebDAVServer;
@protocol GCDWebDAVServerDelegate <NSObject> // These methods are always called on main thread
@protocol GCDWebDAVServerDelegate <GCDWebServerDelegate>
@optional @optional
- (void)davServer:(GCDWebDAVServer*)uploader didDownloadFileAtPath:(NSString*)path; - (void)davServer:(GCDWebDAVServer*)server didDownloadFileAtPath:(NSString*)path;
- (void)davServer:(GCDWebDAVServer*)uploader didUploadFileAtPath:(NSString*)path; - (void)davServer:(GCDWebDAVServer*)server didUploadFileAtPath:(NSString*)path;
- (void)davServer:(GCDWebDAVServer*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; - (void)davServer:(GCDWebDAVServer*)server didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
- (void)davServer:(GCDWebDAVServer*)uploader didCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; - (void)davServer:(GCDWebDAVServer*)server didCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
- (void)davServer:(GCDWebDAVServer*)uploader didDeleteItemAtPath:(NSString*)path; - (void)davServer:(GCDWebDAVServer*)server didDeleteItemAtPath:(NSString*)path;
- (void)davServer:(GCDWebDAVServer*)uploader didCreateDirectoryAtPath:(NSString*)path; - (void)davServer:(GCDWebDAVServer*)server didCreateDirectoryAtPath:(NSString*)path;
@end @end
@interface GCDWebDAVServer : GCDWebServer @interface GCDWebDAVServer : GCDWebServer
@@ -47,6 +48,7 @@
- (instancetype)initWithUploadDirectory:(NSString*)path; - (instancetype)initWithUploadDirectory:(NSString*)path;
@end @end
// These methods can be called from any thread
@interface GCDWebDAVServer (Subclassing) @interface GCDWebDAVServer (Subclassing)
- (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath; // Default implementation returns YES - (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath; // Default implementation returns YES
- (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; // Default implementation returns YES - (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; // Default implementation returns YES

View File

@@ -54,7 +54,6 @@ typedef NS_ENUM(NSInteger, DAVProperties) {
@interface GCDWebDAVServer () { @interface GCDWebDAVServer () {
@private @private
NSString* _uploadDirectory; NSString* _uploadDirectory;
id<GCDWebDAVServerDelegate> __unsafe_unretained _delegate;
NSArray* _allowedExtensions; NSArray* _allowedExtensions;
BOOL _showHidden; BOOL _showHidden;
} }
@@ -102,9 +101,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
return [GCDWebServerResponse response]; return [GCDWebServerResponse response];
} }
if ([_delegate respondsToSelector:@selector(davServer:didDownloadFileAtPath:)]) { if ([self.delegate respondsToSelector:@selector(davServer:didDownloadFileAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didDownloadFileAtPath:absolutePath]; [self.delegate davServer:self didDownloadFileAtPath:absolutePath];
}); });
} }
return [GCDWebServerFileResponse responseWithFile:absolutePath]; return [GCDWebServerFileResponse responseWithFile:absolutePath];
@@ -145,9 +144,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath]; 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(), ^{ 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)]; return [GCDWebServerResponse responseWithStatusCode:(existing ? kGCDWebServerHTTPStatusCode_NoContent : kGCDWebServerHTTPStatusCode_Created)];
@@ -180,9 +179,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath]; 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(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didDeleteItemAtPath:absolutePath]; [self.delegate davServer:self didDeleteItemAtPath:absolutePath];
}); });
} }
return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NoContent]; return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NoContent];
@@ -226,9 +225,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
} }
#endif #endif
if ([_delegate respondsToSelector:@selector(davServer:didCreateDirectoryAtPath:)]) { if ([self.delegate respondsToSelector:@selector(davServer:didCreateDirectoryAtPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didCreateDirectoryAtPath:absolutePath]; [self.delegate davServer:self didCreateDirectoryAtPath:absolutePath];
}); });
} }
return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Created]; return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Created];
@@ -298,15 +297,15 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
} }
if (isMove) { if (isMove) {
if ([_delegate respondsToSelector:@selector(davServer:didMoveItemFromPath:toPath:)]) { if ([self.delegate respondsToSelector:@selector(davServer:didMoveItemFromPath:toPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didMoveItemFromPath:srcAbsolutePath toPath:dstAbsolutePath]; [self.delegate davServer:self didMoveItemFromPath:srcAbsolutePath toPath:dstAbsolutePath];
}); });
} }
} else { } else {
if ([_delegate respondsToSelector:@selector(davServer:didCopyItemFromPath:toPath:)]) { if ([self.delegate respondsToSelector:@selector(davServer:didCopyItemFromPath:toPath:)]) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate davServer:self didCopyItemFromPath:srcAbsolutePath toPath:dstAbsolutePath]; [self.delegate davServer:self didCopyItemFromPath:srcAbsolutePath toPath:dstAbsolutePath];
}); });
} }
} }
@@ -598,7 +597,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
@implementation GCDWebDAVServer @implementation GCDWebDAVServer
@synthesize uploadDirectory=_uploadDirectory, delegate=_delegate, allowedFileExtensions=_allowedExtensions, showHiddenFiles=_showHidden; @synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, showHiddenFiles=_showHidden;
- (instancetype)initWithUploadDirectory:(NSString*)path { - (instancetype)initWithUploadDirectory:(NSString*)path {
if ((self = [super init])) { if ((self = [super init])) {

View File

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

View File

@@ -183,7 +183,7 @@
08FB7794FE84155DC02AAC07 /* LittleCMS */ = { 08FB7794FE84155DC02AAC07 /* LittleCMS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E28BAE1418F99C810095C089 /* CGDWebServer */, E28BAE1418F99C810095C089 /* GCDWebServer */,
E2A0E80718F3432600C580B1 /* GCDWebDAVServer */, E2A0E80718F3432600C580B1 /* GCDWebDAVServer */,
E2BE850618E77ECA0061360B /* GCDWebUploader */, E2BE850618E77ECA0061360B /* GCDWebUploader */,
E221128D1690B6470048D2B2 /* Mac */, E221128D1690B6470048D2B2 /* Mac */,
@@ -247,14 +247,14 @@
name = "Mac Frameworks and Libraries"; name = "Mac Frameworks and Libraries";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E28BAE1418F99C810095C089 /* CGDWebServer */ = { E28BAE1418F99C810095C089 /* GCDWebServer */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E28BAE1518F99C810095C089 /* Core */, E28BAE1518F99C810095C089 /* Core */,
E28BAE2218F99C810095C089 /* Requests */, E28BAE2218F99C810095C089 /* Requests */,
E28BAE2B18F99C810095C089 /* Responses */, E28BAE2B18F99C810095C089 /* Responses */,
); );
path = CGDWebServer; path = GCDWebServer;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E28BAE1518F99C810095C089 /* Core */ = { E28BAE1518F99C810095C089 /* Core */ = {

View File

@@ -42,34 +42,55 @@ typedef NS_ENUM(int, GCDWebServerLogLevel) {
typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery); typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request); typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request);
extern NSString* const GCDWebServerOption_Port; // NSNumber / NSUInteger (default is 0 i.e. use a random port)
extern NSString* const GCDWebServerOption_BonjourName; // NSString (default is empty string i.e. use computer name)
extern NSString* const GCDWebServerOption_MaxPendingConnections; // NSNumber / NSUInteger (default is 16)
extern NSString* const GCDWebServerOption_ServerName; // NSString (default is server class name)
extern NSString* const GCDWebServerOption_AuthenticationMethod; // One of "GCDWebServerAuthenticationMethod_..." (default is nil i.e. no authentication)
extern NSString* const GCDWebServerOption_AuthenticationRealm; // NSString (default is server name)
extern NSString* const GCDWebServerOption_AuthenticationAccounts; // NSDictionary of username / password (default is nil i.e. no accounts)
extern NSString* const GCDWebServerOption_ConnectionClass; // Subclass of GCDWebServerConnection (default is GCDWebServerConnection class)
extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET; // NSNumber / BOOL (default is YES)
extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval; // NSNumber / double (default is 1.0 seconds - set to <=0.0 to disable coaslescing of -webServerDidConnect: / -webServerDidDisconnect:)
#if TARGET_OS_IPHONE
extern NSString* const GCDWebServerOption_AutomaticallySuspendInBackground; // NSNumber / BOOL (default is YES)
#endif
extern NSString* const GCDWebServerAuthenticationMethod_Basic; // Not recommended as password is sent in clear
extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
@class GCDWebServer;
// These methods are always called on main thread
@protocol GCDWebServerDelegate <NSObject>
@optional
- (void)webServerDidStart:(GCDWebServer*)server;
- (void)webServerDidConnect:(GCDWebServer*)server; // Called when first connection is opened
- (void)webServerDidDisconnect:(GCDWebServer*)server; // Called when last connection is closed
- (void)webServerDidStop:(GCDWebServer*)server;
@end
@interface GCDWebServer : NSObject @interface GCDWebServer : NSObject
@property(nonatomic, assign) id<GCDWebServerDelegate> delegate;
@property(nonatomic, readonly, getter=isRunning) BOOL running; @property(nonatomic, readonly, getter=isRunning) BOOL running;
@property(nonatomic, readonly) NSUInteger port; @property(nonatomic, readonly) NSUInteger port; // Only non-zero if running
@property(nonatomic, readonly) NSString* bonjourName; // Only non-nil if Bonjour registration is active @property(nonatomic, readonly) NSString* bonjourName; // Only non-nil if Bonjour registration is active
- (instancetype)init; - (instancetype)init;
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock; - (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
- (void)removeAllHandlers; - (void)removeAllHandlers;
- (BOOL)start; // Default is port 8080 (OS X & iOS Simulator) or 80 (iOS) and computer name - (BOOL)start; // Default is port 8080 (OS X & iOS Simulator) or 80 (iOS) and computer / device name for Bonjour
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name; // Pass nil name to disable Bonjour or empty string to use computer name - (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name; // Pass nil name to disable Bonjour or empty string to use computer name
- (void)stop; - (BOOL)startWithOptions:(NSDictionary*)options;
@end - (void)stop; // Does not abort any currently opened connections
@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 @end
@interface GCDWebServer (Extensions) @interface GCDWebServer (Extensions)
@property(nonatomic, readonly) NSURL* serverURL; // Only non-nil if server is running @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 @property(nonatomic, readonly) NSURL* bonjourServerURL; // Only non-nil if server is running and Bonjour registration is active
#if !TARGET_OS_IPHONE #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 bonjourName:(NSString*)name;
- (BOOL)runWithPort:(NSUInteger)port; // Starts then automatically stops on SIGINT i.e. Ctrl-C (use on main thread only) - (BOOL)runWithOptions:(NSDictionary*)options; // 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 #endif
@end @end
@@ -94,3 +115,12 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
- (void)logWarning:(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); - (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
@end @end
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
@interface GCDWebServer (Testing)
@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)
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path; // Returns number of failed tests or -1 if server failed to start
@end
#endif

View File

@@ -26,6 +26,13 @@
*/ */
#import <TargetConditionals.h> #import <TargetConditionals.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
#import <AppKit/AppKit.h>
#endif
#endif
#import <netinet/in.h> #import <netinet/in.h>
#import "GCDWebServerPrivate.h" #import "GCDWebServerPrivate.h"
@@ -35,16 +42,32 @@
#else #else
#define kDefaultPort 8080 #define kDefaultPort 8080
#endif #endif
#define kMaxPendingConnections 16
@interface GCDWebServer () { @interface GCDWebServer () {
@private @private
id<GCDWebServerDelegate> __unsafe_unretained _delegate;
dispatch_queue_t _syncQueue;
NSMutableArray* _handlers; NSMutableArray* _handlers;
NSInteger _activeConnections; // Accessed only with _syncQueue
BOOL _connected;
CFRunLoopTimerRef _connectedTimer;
NSDictionary* _options;
NSString* _serverName;
NSString* _authenticationRealm;
NSMutableDictionary* _authenticationBasicAccounts;
NSMutableDictionary* _authenticationDigestAccounts;
Class _connectionClass;
BOOL _mapHEADToGET;
CFTimeInterval _disconnectDelay;
NSUInteger _port; NSUInteger _port;
dispatch_source_t _source; dispatch_source_t _source;
CFNetServiceRef _service; CFNetServiceRef _service;
#if !TARGET_OS_IPHONE #if TARGET_OS_IPHONE
BOOL _suspendInBackground;
UIBackgroundTaskIdentifier _backgroundTask;
#endif
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
BOOL _recording; BOOL _recording;
#endif #endif
} }
@@ -57,6 +80,23 @@
} }
@end @end
NSString* const GCDWebServerOption_Port = @"Port";
NSString* const GCDWebServerOption_BonjourName = @"BonjourName";
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
NSString* const GCDWebServerOption_ServerName = @"ServerName";
NSString* const GCDWebServerOption_AuthenticationMethod = @"AuthenticationMethod";
NSString* const GCDWebServerOption_AuthenticationRealm = @"AuthenticationRealm";
NSString* const GCDWebServerOption_AuthenticationAccounts = @"AuthenticationAccounts";
NSString* const GCDWebServerOption_ConnectionClass = @"ConnectionClass";
NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET = @"AutomaticallyMapHEADToGET";
NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval = @"ConnectedStateCoalescingInterval";
#if TARGET_OS_IPHONE
NSString* const GCDWebServerOption_AutomaticallySuspendInBackground = @"AutomaticallySuspendInBackground";
#endif
NSString* const GCDWebServerAuthenticationMethod_Basic = @"Basic";
NSString* const GCDWebServerAuthenticationMethod_DigestAccess = @"DigestAccess";
#ifndef __GCDWEBSERVER_LOGGING_HEADER__ #ifndef __GCDWEBSERVER_LOGGING_HEADER__
#ifdef NDEBUG #ifdef NDEBUG
GCDWebServerLogLevel GCDLogLevel = kGCDWebServerLogLevel_Info; GCDWebServerLogLevel GCDLogLevel = kGCDWebServerLogLevel_Info;
@@ -115,7 +155,9 @@ static void _SignalHandler(int signal) {
@implementation GCDWebServer @implementation GCDWebServer
@synthesize handlers=_handlers, port=_port; @synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm,
authenticationBasicAccounts=_authenticationBasicAccounts, authenticationDigestAccounts=_authenticationDigestAccounts,
shouldAutomaticallyMapHEADToGET=_mapHEADToGET;
#ifndef __GCDWEBSERVER_LOGGING_HEADER__ #ifndef __GCDWEBSERVER_LOGGING_HEADER__
@@ -132,44 +174,166 @@ static void _SignalHandler(int signal) {
GCDWebServerInitializeFunctions(); GCDWebServerInitializeFunctions();
} }
static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
@autoreleasepool {
[(ARC_BRIDGE GCDWebServer*)info _didDisconnect];
}
}
- (instancetype)init { - (instancetype)init {
if ((self = [super init])) { if ((self = [super init])) {
_syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL);
_handlers = [[NSMutableArray alloc] init]; _handlers = [[NSMutableArray alloc] init];
CFRunLoopTimerContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
_connectedTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _ConnectedTimerCallBack, &context);
CFRunLoopAddTimer(CFRunLoopGetMain(), _connectedTimer, kCFRunLoopCommonModes);
#if TARGET_OS_IPHONE
_backgroundTask = UIBackgroundTaskInvalid;
#endif
} }
return self; return self;
} }
- (void)dealloc { - (void)dealloc {
if (_source) { DCHECK(_connected == NO);
DCHECK(_activeConnections == 0);
_delegate = nil;
if (_options) {
[self stop]; [self stop];
} }
CFRunLoopTimerInvalidate(_connectedTimer);
CFRelease(_connectedTimer);
ARC_RELEASE(_handlers); ARC_RELEASE(_handlers);
ARC_DISPATCH_RELEASE(_syncQueue);
ARC_DEALLOC(super); ARC_DEALLOC(super);
} }
#if TARGET_OS_IPHONE
// Always called on main thread
- (void)_startBackgroundTask {
DCHECK([NSThread isMainThread]);
if (_backgroundTask == UIBackgroundTaskInvalid) {
LOG_DEBUG(@"Did start background task");
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]);
[self _endBackgroundTask];
}];
} else {
DNOT_REACHED();
}
}
#endif
// Always called on main thread
- (void)_didConnect {
DCHECK([NSThread isMainThread]);
DCHECK(_connected == NO);
_connected = YES;
LOG_DEBUG(@"Did connect");
#if TARGET_OS_IPHONE
[self _startBackgroundTask];
#endif
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
[_delegate webServerDidConnect:self];
}
}
- (void)willStartConnection:(GCDWebServerConnection*)connection {
dispatch_sync(_syncQueue, ^{
DCHECK(_activeConnections >= 0);
if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnectDelay > 0.0) {
CFRunLoopTimerSetNextFireDate(_connectedTimer, HUGE_VAL);
}
if (_connected == NO) {
[self _didConnect];
}
});
}
_activeConnections += 1;
});
}
#if TARGET_OS_IPHONE
// Always called on main thread
- (void)_endBackgroundTask {
DCHECK([NSThread isMainThread]);
if (_backgroundTask != UIBackgroundTaskInvalid) {
if (_suspendInBackground && ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) && _source) {
[self _stop];
}
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
_backgroundTask = UIBackgroundTaskInvalid;
LOG_DEBUG(@"Did end background task");
} else {
DNOT_REACHED();
}
}
#endif
// Always called on main thread
- (void)_didDisconnect {
DCHECK([NSThread isMainThread]);
DCHECK(_connected == YES);
_connected = NO;
LOG_DEBUG(@"Did disconnect");
#if TARGET_OS_IPHONE
[self _endBackgroundTask];
#endif
if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) {
[_delegate webServerDidDisconnect:self];
}
}
- (void)didEndConnection:(GCDWebServerConnection*)connection {
dispatch_sync(_syncQueue, ^{
DCHECK(_activeConnections > 0);
_activeConnections -= 1;
if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnectDelay > 0.0) {
CFRunLoopTimerSetNextFireDate(_connectedTimer, CFAbsoluteTimeGetCurrent() + _disconnectDelay);
} else {
[self _didDisconnect];
}
});
}
});
}
- (NSString*)bonjourName { - (NSString*)bonjourName {
CFStringRef name = _service ? CFNetServiceGetName(_service) : NULL; CFStringRef name = _service ? CFNetServiceGetName(_service) : NULL;
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil; return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
} }
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)handlerBlock { - (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)handlerBlock {
DCHECK(_source == NULL); DCHECK(_options == nil);
GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock processBlock:handlerBlock]; GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock processBlock:handlerBlock];
[_handlers insertObject:handler atIndex:0]; [_handlers insertObject:handler atIndex:0];
ARC_RELEASE(handler); ARC_RELEASE(handler);
} }
- (void)removeAllHandlers { - (void)removeAllHandlers {
DCHECK(_source == NULL); DCHECK(_options == nil);
[_handlers removeAllObjects]; [_handlers removeAllObjects];
} }
- (BOOL)start {
return [self startWithPort:kDefaultPort bonjourName:@""];
}
static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* error, void* info) { static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* error, void* info) {
@autoreleasepool { @autoreleasepool {
if (error->error) { if (error->error) {
@@ -181,8 +345,25 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
} }
} }
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name { static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValue) {
id value = [options objectForKey:key];
return value ? value : defaultValue;
}
static inline NSString* _EncodeBase64(NSString* string) {
NSData* data = [string dataUsingEncoding:NSUTF8StringEncoding];
#if (TARGET_OS_IPHONE && !(__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0)) || (!TARGET_OS_IPHONE && !(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9))
if (![data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) {
return [data base64Encoding];
}
#endif
return ARC_AUTORELEASE([[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding]);
}
- (BOOL)_start {
DCHECK(_source == NULL); DCHECK(_source == NULL);
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
NSString* name = _GetOption(_options, GCDWebServerOption_BonjourName, @"");
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
int listeningSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); int listeningSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listeningSocket > 0) { if (listeningSocket > 0) {
int yes = 1; int yes = 1;
@@ -195,16 +376,37 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
addr4.sin_port = htons(port); addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = htonl(INADDR_ANY); addr4.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listeningSocket, (void*)&addr4, sizeof(addr4)) == 0) { if (bind(listeningSocket, (void*)&addr4, sizeof(addr4)) == 0) {
if (listen(listeningSocket, kMaxPendingConnections) == 0) { if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
LOG_DEBUG(@"Did open listening socket %i", listeningSocket);
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationBasicAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username];
}];
} else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationDigestAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, _authenticationRealm, password) forKey:username];
}];
}
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, kGCDWebServerGCDQueue); _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, kGCDWebServerGCDQueue);
dispatch_source_set_cancel_handler(_source, ^{ dispatch_source_set_cancel_handler(_source, ^{
@autoreleasepool { @autoreleasepool {
int result = close(listeningSocket); int result = close(listeningSocket);
if (result != 0) { if (result != 0) {
LOG_ERROR(@"Failed closing socket (%i): %s", errno, strerror(errno)); LOG_ERROR(@"Failed closing listening socket: %s (%i)", strerror(errno), errno);
} else { } else {
LOG_DEBUG(@"Closed listening socket"); LOG_DEBUG(@"Did close listening socket %i", listeningSocket);
} }
} }
@@ -230,28 +432,27 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
int noSigPipe = 1; int noSigPipe = 1;
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE
Class connectionClass = [[self class] connectionClass]; GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
GCDWebServerConnection* connection = [[connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
#if __has_feature(objc_arc) #if __has_feature(objc_arc)
[connection self]; // Prevent compiler from complaining about unused variable / useless statement [connection self]; // Prevent compiler from complaining about unused variable / useless statement
#else #else
[connection release]; [connection release];
#endif #endif
} else { } else {
LOG_ERROR(@"Failed accepting socket (%i): %s", errno, strerror(errno)); LOG_ERROR(@"Failed accepting socket: %s (%i)", strerror(errno), errno);
} }
} }
}); });
if (port == 0) { // Determine the actual port we are listening on if (port == 0) {
struct sockaddr addr; struct sockaddr addr;
socklen_t addrlen = sizeof(addr); socklen_t addrlen = sizeof(addr);
if (getsockname(listeningSocket, &addr, &addrlen) == 0) { if (getsockname(listeningSocket, &addr, &addrlen) == 0) {
struct sockaddr_in* sockaddr = (struct sockaddr_in*)&addr; struct sockaddr_in* sockaddr = (struct sockaddr_in*)&addr;
_port = ntohs(sockaddr->sin_port); _port = ntohs(sockaddr->sin_port);
} else { } else {
LOG_ERROR(@"Failed retrieving socket address (%i): %s", errno, strerror(errno)); LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
} }
} else { } else {
_port = port; _port = port;
@@ -272,75 +473,141 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
dispatch_resume(_source); dispatch_resume(_source);
LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL); LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
if ([_delegate respondsToSelector:@selector(webServerDidStart:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webServerDidStart:self];
});
}
} else { } else {
LOG_ERROR(@"Failed listening on socket (%i): %s", errno, strerror(errno)); LOG_ERROR(@"Failed listening on socket: %s (%i)", strerror(errno), errno);
close(listeningSocket); close(listeningSocket);
} }
} else { } else {
LOG_ERROR(@"Failed binding socket (%i): %s", errno, strerror(errno)); LOG_ERROR(@"Failed binding socket: %s (%i)", strerror(errno), errno);
close(listeningSocket); close(listeningSocket);
} }
} else { } else {
LOG_ERROR(@"Failed creating socket (%i): %s", errno, strerror(errno)); LOG_ERROR(@"Failed creating socket: %s (%i)", strerror(errno), errno);
} }
return (_source ? YES : NO); return (_source ? YES : NO);
} }
- (void)_stop {
DCHECK(_source != NULL);
if (_service) {
CFNetServiceUnscheduleFromRunLoop(_service, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFNetServiceSetClient(_service, NULL, NULL);
CFRelease(_service);
_service = NULL;
}
dispatch_source_cancel(_source); // This will close the socket
ARC_DISPATCH_RELEASE(_source);
_source = NULL;
_port = 0;
ARC_RELEASE(_serverName);
_serverName = nil;
ARC_RELEASE(_authenticationRealm);
_authenticationRealm = nil;
ARC_RELEASE(_authenticationBasicAccounts);
_authenticationBasicAccounts = nil;
ARC_RELEASE(_authenticationDigestAccounts);
_authenticationDigestAccounts = nil;
LOG_INFO(@"%@ stopped", [self class]);
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webServerDidStop:self];
});
}
}
- (BOOL)start {
return [self startWithPort:kDefaultPort bonjourName:@""];
}
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name {
NSMutableDictionary* options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithInteger:port] forKey:GCDWebServerOption_Port];
[options setValue:name forKey:GCDWebServerOption_BonjourName];
return [self startWithOptions:options];
}
#if TARGET_OS_IPHONE
- (void)_didEnterBackground:(NSNotification*)notification {
DCHECK([NSThread isMainThread]);
LOG_DEBUG(@"Did enter background");
if ((_backgroundTask == UIBackgroundTaskInvalid) && _source) {
[self _stop];
}
}
- (void)_willEnterForeground:(NSNotification*)notification {
DCHECK([NSThread isMainThread]);
LOG_DEBUG(@"Will enter foreground");
if (!_source) {
[self _start]; // TODO: There's probably nothing we can do on failure
}
}
#endif
- (BOOL)startWithOptions:(NSDictionary*)options {
if (_options == nil) {
_options = [options copy];
#if TARGET_OS_IPHONE
_suspendInBackground = [_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue];
if (((_suspendInBackground == NO) || ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground)) && ![self _start])
#else
if (![self _start])
#endif
{
ARC_RELEASE(_options);
_options = nil;
return NO;
}
#if TARGET_OS_IPHONE
if (_suspendInBackground) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
#endif
return YES;
} else {
DNOT_REACHED();
}
return NO;
}
- (BOOL)isRunning { - (BOOL)isRunning {
return (_source ? YES : NO); return (_source ? YES : NO);
} }
- (void)stop { - (void)stop {
DCHECK(_source != NULL); if (_options) {
if (_source) { #if TARGET_OS_IPHONE
if (_service) { if (_suspendInBackground) {
CFNetServiceUnscheduleFromRunLoop(_service, CFRunLoopGetMain(), kCFRunLoopCommonModes); [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
CFNetServiceSetClient(_service, NULL, NULL); [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
CFRelease(_service);
_service = NULL;
} }
#endif
dispatch_source_cancel(_source); // This will close the socket if (_source) {
ARC_DISPATCH_RELEASE(_source); [self _stop];
_source = NULL; }
ARC_RELEASE(_options);
LOG_INFO(@"%@ stopped", [self class]); _options = nil;
} else {
DNOT_REACHED();
} }
_port = 0;
}
@end
@implementation GCDWebServer (Subclassing)
+ (Class)connectionClass {
return [GCDWebServerConnection class];
}
+ (NSString*)serverName {
return NSStringFromClass(self);
}
+ (BOOL)shouldAutomaticallyMapHEADToGET {
return YES;
} }
@end @end
@implementation GCDWebServer (Extensions) @implementation GCDWebServer (Extensions)
#if !TARGET_OS_IPHONE
- (void)setRecordingEnabled:(BOOL)flag {
_recording = flag;
}
- (BOOL)isRecordingEnabled {
return _recording;
}
#endif
- (NSURL*)serverURL { - (NSURL*)serverURL {
if (_source) { if (_source) {
NSString* ipAddress = GCDWebServerGetPrimaryIPv4Address(); NSString* ipAddress = GCDWebServerGetPrimaryIPv4Address();
@@ -371,12 +638,20 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
#if !TARGET_OS_IPHONE #if !TARGET_OS_IPHONE
- (BOOL)runWithPort:(NSUInteger)port { - (BOOL)runWithPort:(NSUInteger)port bonjourName:(NSString*)name {
NSMutableDictionary* options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithInteger:port] forKey:GCDWebServerOption_Port];
[options setValue:name forKey:GCDWebServerOption_BonjourName];
return [self runWithOptions:options];
}
- (BOOL)runWithOptions:(NSDictionary*)options {
DCHECK([NSThread isMainThread]);
BOOL success = NO; BOOL success = NO;
_run = YES; _run = YES;
void (*handler)(int) = signal(SIGINT, _SignalHandler); void (*handler)(int) = signal(SIGINT, _SignalHandler);
if (handler != SIG_ERR) { if (handler != SIG_ERR) {
if ([self startWithPort:port bonjourName:@""]) { if ([self startWithOptions:options]) {
while (_run) { while (_run) {
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0, true); CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0, true);
} }
@@ -390,175 +665,6 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
#endif #endif
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
static CFHTTPMessageRef _CreateHTTPMessageFromData(NSData* data, BOOL isRequest) {
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, isRequest);
if (CFHTTPMessageAppendBytes(message, data.bytes, data.length)) {
return message;
}
CFRelease(message);
return NULL;
}
static CFHTTPMessageRef _CreateHTTPMessageFromPerformingRequest(NSData* inData, NSUInteger port) {
CFHTTPMessageRef response = NULL;
int httpSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (httpSocket > 0) {
struct sockaddr_in addr4;
bzero(&addr4, sizeof(addr4));
addr4.sin_len = sizeof(port);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(8080);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
if (connect(httpSocket, (void*)&addr4, sizeof(addr4)) == 0) {
if (write(httpSocket, inData.bytes, inData.length) == (ssize_t)inData.length) {
NSMutableData* outData = [[NSMutableData alloc] initWithLength:(256 * 1024)];
NSUInteger length = 0;
while (1) {
ssize_t result = read(httpSocket, (char*)outData.mutableBytes + length, outData.length - length);
if (result < 0) {
length = NSNotFound;
break;
} else if (result == 0) {
break;
}
length += result;
if (length >= outData.length) {
outData.length = 2 * outData.length;
}
}
if (length != NSNotFound) {
outData.length = length;
response = _CreateHTTPMessageFromData(outData, NO);
} else {
DNOT_REACHED();
}
ARC_RELEASE(outData);
}
}
close(httpSocket);
}
return response;
}
static void _LogResult(NSString* format, ...) {
va_list arguments;
va_start(arguments, format);
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
va_end(arguments);
fprintf(stdout, "%s\n", [message UTF8String]);
ARC_RELEASE(message);
}
- (NSInteger)runTestsInDirectory:(NSString*)path withPort:(NSUInteger)port {
NSArray* ignoredHeaders = @[@"Date", @"Etag"]; // Dates are always different by definition and ETags depend on file system node IDs
NSInteger result = -1;
if ([self startWithPort:port bonjourName:nil]) {
result = 0;
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
for (NSString* requestFile in files) {
if (![requestFile hasSuffix:@".request"]) {
continue;
}
@autoreleasepool {
NSString* index = [[requestFile componentsSeparatedByString:@"-"] firstObject];
BOOL success = NO;
NSData* requestData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:requestFile]];
if (requestData) {
CFHTTPMessageRef request = _CreateHTTPMessageFromData(requestData, YES);
if (request) {
NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(request));
NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(request));
_LogResult(@"[%i] %@ %@", (int)[index integerValue], requestMethod, requestURL.path);
NSString* prefix = [index stringByAppendingString:@"-"];
for (NSString* responseFile in files) {
if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]];
if (responseData) {
CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
if (expectedResponse) {
CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, port);
if (actualResponse) {
success = YES;
CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
if (actualStatusCode != expectedStatusCode) {
_LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
success = NO;
}
NSDictionary* expectedHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
NSDictionary* actualHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(actualResponse));
for (NSString* expectedHeader in expectedHeaders) {
if ([ignoredHeaders containsObject:expectedHeader]) {
continue;
}
NSString* expectedValue = [expectedHeaders objectForKey:expectedHeader];
NSString* actualValue = [actualHeaders objectForKey:expectedHeader];
if (![actualValue isEqualToString:expectedValue]) {
_LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", expectedHeader, expectedValue, actualValue);
success = NO;
}
}
for (NSString* actualHeader in actualHeaders) {
if (![expectedHeaders objectForKey:actualHeader]) {
_LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", actualHeader, nil, [actualHeaders objectForKey:actualHeader]);
success = NO;
}
}
NSData* expectedBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(expectedResponse));
NSData* actualBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(actualResponse));
if (![actualBody isEqualToData:expectedBody]) {
_LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
success = NO;
#ifndef NDEBUG
if (GCDWebServerIsTextContentType([expectedHeaders objectForKey:@"Content-Type"])) {
NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
NSTask* task = [[NSTask alloc] init];
[task setLaunchPath:@"/usr/bin/opendiff"];
[task setArguments:@[expectedPath, actualPath]];
[task launch];
ARC_RELEASE(task);
}
}
#endif
}
CFRelease(actualResponse);
}
CFRelease(expectedResponse);
}
} else {
DNOT_REACHED();
}
break;
}
}
CFRelease(request);
}
} else {
DNOT_REACHED();
}
_LogResult(@"");
if (!success) {
++result;
}
}
}
[self stop];
}
return result;
}
#endif
@end @end
@implementation GCDWebServer (Handlers) @implementation GCDWebServer (Handlers)
@@ -770,3 +876,186 @@ static void _LogResult(NSString* format, ...) {
} }
@end @end
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
@implementation GCDWebServer (Testing)
- (void)setRecordingEnabled:(BOOL)flag {
_recording = flag;
}
- (BOOL)isRecordingEnabled {
return _recording;
}
static CFHTTPMessageRef _CreateHTTPMessageFromData(NSData* data, BOOL isRequest) {
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, isRequest);
if (CFHTTPMessageAppendBytes(message, data.bytes, data.length)) {
return message;
}
CFRelease(message);
return NULL;
}
static CFHTTPMessageRef _CreateHTTPMessageFromPerformingRequest(NSData* inData, NSUInteger port) {
CFHTTPMessageRef response = NULL;
int httpSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (httpSocket > 0) {
struct sockaddr_in addr4;
bzero(&addr4, sizeof(addr4));
addr4.sin_len = sizeof(port);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(8080);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
if (connect(httpSocket, (void*)&addr4, sizeof(addr4)) == 0) {
if (write(httpSocket, inData.bytes, inData.length) == (ssize_t)inData.length) {
NSMutableData* outData = [[NSMutableData alloc] initWithLength:(256 * 1024)];
NSUInteger length = 0;
while (1) {
ssize_t result = read(httpSocket, (char*)outData.mutableBytes + length, outData.length - length);
if (result < 0) {
length = NSNotFound;
break;
} else if (result == 0) {
break;
}
length += result;
if (length >= outData.length) {
outData.length = 2 * outData.length;
}
}
if (length != NSNotFound) {
outData.length = length;
response = _CreateHTTPMessageFromData(outData, NO);
} else {
DNOT_REACHED();
}
ARC_RELEASE(outData);
}
}
close(httpSocket);
}
return response;
}
static void _LogResult(NSString* format, ...) {
va_list arguments;
va_start(arguments, format);
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
va_end(arguments);
fprintf(stdout, "%s\n", [message UTF8String]);
ARC_RELEASE(message);
}
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path {
NSArray* ignoredHeaders = @[@"Date", @"Etag"]; // Dates are always different by definition and ETags depend on file system node IDs
NSInteger result = -1;
if ([self startWithOptions:options]) {
result = 0;
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
for (NSString* requestFile in files) {
if (![requestFile hasSuffix:@".request"]) {
continue;
}
@autoreleasepool {
NSString* index = [[requestFile componentsSeparatedByString:@"-"] firstObject];
BOOL success = NO;
NSData* requestData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:requestFile]];
if (requestData) {
CFHTTPMessageRef request = _CreateHTTPMessageFromData(requestData, YES);
if (request) {
NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(request));
NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(request));
_LogResult(@"[%i] %@ %@", (int)[index integerValue], requestMethod, requestURL.path);
NSString* prefix = [index stringByAppendingString:@"-"];
for (NSString* responseFile in files) {
if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]];
if (responseData) {
CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
if (expectedResponse) {
CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, self.port);
if (actualResponse) {
success = YES;
CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
if (actualStatusCode != expectedStatusCode) {
_LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
success = NO;
}
NSDictionary* expectedHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
NSDictionary* actualHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(actualResponse));
for (NSString* expectedHeader in expectedHeaders) {
if ([ignoredHeaders containsObject:expectedHeader]) {
continue;
}
NSString* expectedValue = [expectedHeaders objectForKey:expectedHeader];
NSString* actualValue = [actualHeaders objectForKey:expectedHeader];
if (![actualValue isEqualToString:expectedValue]) {
_LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", expectedHeader, expectedValue, actualValue);
success = NO;
}
}
for (NSString* actualHeader in actualHeaders) {
if (![expectedHeaders objectForKey:actualHeader]) {
_LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", actualHeader, nil, [actualHeaders objectForKey:actualHeader]);
success = NO;
}
}
NSData* expectedBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(expectedResponse));
NSData* actualBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(actualResponse));
if (![actualBody isEqualToData:expectedBody]) {
_LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
success = NO;
#if !TARGET_OS_IPHONE
#ifndef NDEBUG
if (GCDWebServerIsTextContentType([expectedHeaders objectForKey:@"Content-Type"])) {
NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
NSTask* task = [[NSTask alloc] init];
[task setLaunchPath:@"/usr/bin/opendiff"];
[task setArguments:@[expectedPath, actualPath]];
[task launch];
ARC_RELEASE(task);
}
}
#endif
#endif
}
CFRelease(actualResponse);
}
CFRelease(expectedResponse);
}
} else {
DNOT_REACHED();
}
break;
}
}
CFRelease(request);
}
} else {
DNOT_REACHED();
}
_LogResult(@"");
if (!success) {
++result;
}
}
}
[self stop];
}
return result;
}
@end
#endif

View File

@@ -39,12 +39,14 @@
@property(nonatomic, readonly) NSUInteger totalBytesWritten; @property(nonatomic, readonly) NSUInteger totalBytesWritten;
@end @end
// These methods can be called from any thread
@interface GCDWebServerConnection (Subclassing) @interface GCDWebServerConnection (Subclassing)
- (void)open; - (BOOL)open; // Return NO to reject connection e.g. after validating local or remote addresses
- (void)didUpdateBytesRead; // Called from arbitrary thread after @totalBytesRead is updated - Default implementation does nothing - (void)didReadBytes:(const void*)bytes length:(NSUInteger)length; // Called after data has been read from the connection
- (void)didUpdateBytesWritten; // Called from arbitrary thread after @totalBytesWritten is updated - Default implementation does nothing - (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length; // Called after data has been written to the connection
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request; // Called before request is processed to return an override response bypassing processing or nil to continue - Default implementation checks authentication if applicable
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block; // Only called if the request can be processed - (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 - (GCDWebServerResponse*)overrideResponse:(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)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode; // If request headers were malformed, "request" will be nil
- (void)close; - (void)close;
@end @end

View File

@@ -27,20 +27,19 @@
#import <TargetConditionals.h> #import <TargetConditionals.h>
#import <netdb.h> #import <netdb.h>
#if !TARGET_OS_IPHONE #ifdef __GCDWEBSERVER_ENABLE_TESTING__
#import <libkern/OSAtomic.h> #import <libkern/OSAtomic.h>
#endif #endif
#import "GCDWebServerPrivate.h" #import "GCDWebServerPrivate.h"
#define kHeadersReadBuffer 1024 #define kHeadersReadCapacity (1 * 1024)
#define kBodyReadCapacity (256 * 1024)
typedef void (^ReadBufferCompletionBlock)(dispatch_data_t buffer); typedef void (^ReadDataCompletionBlock)(BOOL success);
typedef void (^ReadDataCompletionBlock)(NSData* data);
typedef void (^ReadHeadersCompletionBlock)(NSData* extraData); typedef void (^ReadHeadersCompletionBlock)(NSData* extraData);
typedef void (^ReadBodyCompletionBlock)(BOOL success); typedef void (^ReadBodyCompletionBlock)(BOOL success);
typedef void (^WriteBufferCompletionBlock)(BOOL success);
typedef void (^WriteDataCompletionBlock)(BOOL success); typedef void (^WriteDataCompletionBlock)(BOOL success);
typedef void (^WriteHeadersCompletionBlock)(BOOL success); typedef void (^WriteHeadersCompletionBlock)(BOOL success);
typedef void (^WriteBodyCompletionBlock)(BOOL success); typedef void (^WriteBodyCompletionBlock)(BOOL success);
@@ -49,7 +48,8 @@ static NSData* _CRLFData = nil;
static NSData* _CRLFCRLFData = nil; static NSData* _CRLFCRLFData = nil;
static NSData* _continueData = nil; static NSData* _continueData = nil;
static NSData* _lastChunkData = nil; static NSData* _lastChunkData = nil;
#if !TARGET_OS_IPHONE static NSString* _digestAuthenticationNonce = nil;
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
static int32_t _connectionCounter = 0; static int32_t _connectionCounter = 0;
#endif #endif
@@ -70,7 +70,8 @@ static int32_t _connectionCounter = 0;
GCDWebServerResponse* _response; GCDWebServerResponse* _response;
NSInteger _statusCode; NSInteger _statusCode;
#if !TARGET_OS_IPHONE BOOL _opened;
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
NSUInteger _connectionIndex; NSUInteger _connectionIndex;
NSString* _requestPath; NSString* _requestPath;
int _requestFD; int _requestFD;
@@ -82,87 +83,50 @@ static int32_t _connectionCounter = 0;
@implementation GCDWebServerConnection (Read) @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) { dispatch_read(_socket, length, kGCDWebServerGCDQueue, ^(dispatch_data_t buffer, int error) {
@autoreleasepool { @autoreleasepool {
if (error == 0) { if (error == 0) {
size_t size = dispatch_data_get_size(buffer); size_t size = dispatch_data_get_size(buffer);
if (size > 0) { if (size > 0) {
LOG_DEBUG(@"Connection received %zu bytes on socket %i", size, _socket); NSUInteger originalLength = data.length;
_bytesRead += size; dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
[self didUpdateBytesRead]; [data appendBytes:chunkBytes length:chunkSize];
#if !TARGET_OS_IPHONE return true;
if (_requestFD > 0) { });
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) { [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
return (write(_requestFD, chunkBytes, chunkSize) == (ssize_t)chunkSize); block(YES);
});
if (!success) {
LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
close(_requestFD);
_requestFD = 0;
}
}
#endif
block(buffer);
} else { } else {
if (_bytesRead > 0) { if (_bytesRead > 0) {
LOG_ERROR(@"No more data available on socket %i", _socket); LOG_ERROR(@"No more data available on socket %i", _socket);
} else { } else {
LOG_WARNING(@"No data received from socket %i", _socket); LOG_WARNING(@"No data received from socket %i", _socket);
} }
block(NULL); block(NO);
} }
} else { } else {
LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error); LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error);
block(NULL); block(NO);
} }
} }
}); });
} }
- (void)_readDataWithCompletionBlock:(ReadDataCompletionBlock)block { - (void)_readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)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 {
DCHECK(_requestMessage); DCHECK(_requestMessage);
[self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) { [self _readData:headersData withLength:NSUIntegerMax completionBlock:^(BOOL success) {
if (buffer) { if (success) {
NSMutableData* data = [NSMutableData dataWithCapacity:kHeadersReadBuffer]; NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.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;
});
NSRange range = [data rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, data.length)];
if (range.location == NSNotFound) { if (range.location == NSNotFound) {
if (CFHTTPMessageAppendBytes(_requestMessage, data.bytes, data.length)) { [self _readHeaders:headersData withCompletionBlock:block];
[self _readHeadersWithCompletionBlock:block];
} else {
LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
block(nil);
}
} else { } else {
NSUInteger length = range.location + range.length; NSUInteger length = range.location + range.length;
if (CFHTTPMessageAppendBytes(_requestMessage, data.bytes, length)) { if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
if (CFHTTPMessageIsHeaderComplete(_requestMessage)) { if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
block([data subdataWithRange:NSMakeRange(length, data.length - length)]); block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
} else { } else {
LOG_ERROR(@"Failed parsing request headers from socket %i", _socket); LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
block(nil); block(nil);
@@ -181,27 +145,21 @@ static int32_t _connectionCounter = 0;
- (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block { - (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]); 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 (success) {
if (dispatch_data_get_size(buffer) <= length) { if (bodyData.length <= length) {
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) { NSError* error = nil;
NSData* data = [NSData dataWithBytesNoCopy:(void*)chunkBytes length:chunkSize freeWhenDone:NO]; if ([_request performWriteData:bodyData error:&error]) {
NSError* error = nil; NSUInteger remainingLength = length - bodyData.length;
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 (remainingLength) { if (remainingLength) {
[self _readBodyWithRemainingLength:remainingLength completionBlock:block]; [self _readBodyWithRemainingLength:remainingLength completionBlock:block];
} else { } else {
block(YES); block(YES);
} }
} else { } else {
LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
block(NO); block(NO);
} }
} else { } else {
@@ -214,6 +172,7 @@ static int32_t _connectionCounter = 0;
} }
}]; }];
ARC_RELEASE(bodyData);
} }
static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { 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) { if (success) {
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;
});
[self _readNextBodyChunk:chunkData completionBlock:block]; [self _readNextBodyChunk:chunkData completionBlock:block];
} else { } else {
block(NO); block(NO);
@@ -288,44 +243,6 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
@implementation GCDWebServerConnection (Write) @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 { - (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
#if !__has_feature(objc_arc) #if !__has_feature(objc_arc)
[data retain]; [data retain];
@@ -337,15 +254,28 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
[data release]; [data release];
#endif #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); ARC_DISPATCH_RELEASE(buffer);
} }
- (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block { - (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
DCHECK(_responseMessage); DCHECK(_responseMessage);
CFDataRef message = CFHTTPMessageCopySerializedMessage(_responseMessage); CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage);
[self _writeData:(ARC_BRIDGE NSData*)message withCompletionBlock:block]; [self _writeData:(ARC_BRIDGE NSData*)data withCompletionBlock:block];
CFRelease(message); CFRelease(data);
} }
- (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block { - (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
@@ -428,13 +358,18 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
if (_lastChunkData == nil) { if (_lastChunkData == nil) {
_lastChunkData = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5]; _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 { - (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
_statusCode = statusCode; _statusCode = statusCode;
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1); _responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close")); 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])); 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); DCHECK(_responseMessage == NULL);
BOOL hasBody = NO; 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) { if (response) {
response = [self replaceResponse:response forRequest:_request]; response = [self overrideResponse:response forRequest:_request];
if (response) { }
if ([response hasBody]) { if (response) {
[response prepareForReading]; if ([response hasBody]) {
hasBody = !_virtualHEAD; [response prepareForReading];
} hasBody = !_virtualHEAD;
NSError* error = nil; }
if (hasBody && ![response performOpen:&error]) { NSError* error = nil;
LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error); if (hasBody && ![response performOpen:&error]) {
} else { LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error);
_response = ARC_RETAIN(response); } else {
} _response = ARC_RETAIN(response);
} }
} }
@@ -575,11 +513,12 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
- (void)_readRequestHeaders { - (void)_readRequestHeaders {
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true); _requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
[self _readHeadersWithCompletionBlock:^(NSData* extraData) { NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
[self _readHeaders:headersData withCompletionBlock:^(NSData* extraData) {
if (extraData) { if (extraData) {
NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase
if ([[_server class] shouldAutomaticallyMapHEADToGET] && [requestMethod isEqualToString:@"HEAD"]) { if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
requestMethod = @"GET"; requestMethod = @"GET";
_virtualHEAD = YES; _virtualHEAD = YES;
} }
@@ -599,9 +538,9 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
if ([_request hasBody]) { if ([_request hasBody]) {
[_request prepareForWriting]; [_request prepareForWriting];
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) { if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
NSString* expectHeader = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyHeaderFieldValue(_requestMessage, CFSTR("Expect"))); NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
if (expectHeader) { if (expectHeader) {
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
[self _writeData:_continueData withCompletionBlock:^(BOOL success) { [self _writeData:_continueData withCompletionBlock:^(BOOL success) {
if (success) { if (success) {
@@ -645,6 +584,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
} }
}]; }];
ARC_RELEASE(headersData);
} }
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket { - (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
@@ -653,8 +593,18 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
_localAddress = ARC_RETAIN(localAddress); _localAddress = ARC_RETAIN(localAddress);
_remoteAddress = ARC_RETAIN(remoteAddress); _remoteAddress = ARC_RETAIN(remoteAddress);
_socket = socket; _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; return self;
} }
@@ -681,8 +631,18 @@ static NSString* _StringFromAddressData(NSData* data) {
} }
- (void)dealloc { - (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(_server);
ARC_RELEASE(_localAddress); ARC_RELEASE(_localAddress);
ARC_RELEASE(_remoteAddress); ARC_RELEASE(_remoteAddress);
@@ -697,7 +657,7 @@ static NSString* _StringFromAddressData(NSData* data) {
} }
ARC_RELEASE(_response); ARC_RELEASE(_response);
#if !TARGET_OS_IPHONE #ifdef __GCDWEBSERVER_ENABLE_TESTING__
ARC_RELEASE(_requestPath); ARC_RELEASE(_requestPath);
ARC_RELEASE(_responsePath); ARC_RELEASE(_responsePath);
#endif #endif
@@ -709,10 +669,8 @@ static NSString* _StringFromAddressData(NSData* data) {
@implementation GCDWebServerConnection (Subclassing) @implementation GCDWebServerConnection (Subclassing)
- (void)open { - (BOOL)open {
LOG_DEBUG(@"Did open connection on socket %i", _socket); #ifdef __GCDWEBSERVER_ENABLE_TESTING__
#if !TARGET_OS_IPHONE
if (_server.recordingEnabled) { if (_server.recordingEnabled) {
_connectionIndex = OSAtomicIncrement32(&_connectionCounter); _connectionIndex = OSAtomicIncrement32(&_connectionCounter);
@@ -726,15 +684,84 @@ static NSString* _StringFromAddressData(NSData* data) {
} }
#endif #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 { - (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {
@@ -764,7 +791,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
return NO; 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)) { 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; NSInteger code = [request.method isEqualToString:@"HEAD"] || [request.method isEqualToString:@"GET"] ? kGCDWebServerHTTPStatusCode_NotModified : kGCDWebServerHTTPStatusCode_PreconditionFailed;
GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code]; GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code];
@@ -788,14 +815,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
} }
- (void)close { - (void)close {
int result = close(_socket); #ifdef __GCDWEBSERVER_ENABLE_TESTING__
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
if (_requestPath) { if (_requestPath) {
BOOL success = NO; BOOL success = NO;
NSError* error = nil; NSError* error = nil;
@@ -826,6 +846,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
unlink([_responsePath fileSystemRepresentation]); unlink([_responsePath fileSystemRepresentation]);
} }
#endif #endif
if (_request) { 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); LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
} else { } else {

View File

@@ -31,6 +31,7 @@
#else #else
#import <SystemConfiguration/SystemConfiguration.h> #import <SystemConfiguration/SystemConfiguration.h>
#endif #endif
#import <CommonCrypto/CommonDigest.h>
#import <ifaddrs.h> #import <ifaddrs.h>
#import <net/if.h> #import <net/if.h>
@@ -79,13 +80,11 @@ NSString* GCDWebServerNormalizeHeaderValue(NSString* value) {
} }
NSString* GCDWebServerTruncateHeaderValue(NSString* value) { NSString* GCDWebServerTruncateHeaderValue(NSString* value) {
DCHECK([value isEqualToString:GCDWebServerNormalizeHeaderValue(value)]);
NSRange range = [value rangeOfString:@";"]; NSRange range = [value rangeOfString:@";"];
return range.location != NSNotFound ? [value substringToIndex:range.location] : value; return range.location != NSNotFound ? [value substringToIndex:range.location] : value;
} }
NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) { NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) {
DCHECK([value isEqualToString:GCDWebServerNormalizeHeaderValue(value)]);
NSString* parameter = nil; NSString* parameter = nil;
NSScanner* scanner = [[NSScanner alloc] initWithString:value]; NSScanner* scanner = [[NSScanner alloc] initWithString:value];
[scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive [scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive
@@ -266,3 +265,22 @@ NSString* GCDWebServerGetPrimaryIPv4Address() {
} }
return address; 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

@@ -121,6 +121,7 @@ extern NSString* GCDWebServerExtractHeaderValueParameter(NSString* header, NSStr
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset); extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
extern BOOL GCDWebServerIsTextContentType(NSString* type); extern BOOL GCDWebServerIsTextContentType(NSString* type);
extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType); extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1,2);
@interface GCDWebServerConnection () @interface GCDWebServerConnection ()
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket; - (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
@@ -128,6 +129,13 @@ extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
@interface GCDWebServer () @interface GCDWebServer ()
@property(nonatomic, readonly) NSArray* handlers; @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 @end
@interface GCDWebServerHandler : NSObject @interface GCDWebServerHandler : NSObject

View File

@@ -43,7 +43,7 @@
@property(nonatomic, readonly) NSUInteger contentLength; // Automatically parsed from headers (NSNotFound if request has no "Content-Length" 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) 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) 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) NSRange byteRange; // Automatically parsed from headers ([NSNotFound, 0] if request has no "Range" header, [offset, length] for byte range from beginning or [NSNotFound, -length] from end)
@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding; // Automatically parsed from headers @property(nonatomic, readonly) BOOL acceptsGzipContentEncoding; // Automatically parsed from headers
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query; - (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)hasBody; // Convenience method that checks if "contentType" is not nil

View File

@@ -28,7 +28,7 @@
#import "GCDWebServerRequest.h" #import "GCDWebServerRequest.h"
@interface GCDWebServerMultiPart : NSObject @interface GCDWebServerMultiPart : NSObject
@property(nonatomic, readonly) NSString* contentType; // Defaults to "text/plain" per specifications if undefined @property(nonatomic, readonly) NSString* contentType; // Defaults to "text/plain" per specification if undefined
@property(nonatomic, readonly) NSString* mimeType; @property(nonatomic, readonly) NSString* mimeType;
@end @end

View File

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

View File

@@ -33,7 +33,7 @@
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range; + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range;
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
- (instancetype)initWithFile:(NSString*)path; - (instancetype)initWithFile:(NSString*)path;
- (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment; - (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment; // If in attachment mode, "Content-Disposition" header will be set accordingly
- (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 - (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, -length] from end of file
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
@end @end

View File

@@ -112,12 +112,14 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
_size = (NSUInteger)info.st_size; _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 if (attachment) {
NSData* data = [[path lastPathComponent] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]; NSString* fileName = [path lastPathComponent];
NSString* fileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil; NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
if (fileName) { NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
[self setValue:[NSString stringWithFormat:@"attachment; filename=\"%@\"", fileName] forAdditionalHeader:@"Content-Disposition"]; if (lossyFileName) {
ARC_RELEASE(fileName); NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)];
[self setValue:value forAdditionalHeader:@"Content-Disposition"];
ARC_RELEASE(lossyFileName);
} else { } else {
DNOT_REACHED(); DNOT_REACHED();
} }

View File

@@ -29,7 +29,7 @@
typedef NSData* (^GCDWebServerStreamBlock)(NSError** error); typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
@interface GCDWebServerStreamingResponse : GCDWebServerResponse // Automatically enables chunked transfer encoding @interface GCDWebServerStreamingResponse : GCDWebServerResponse
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; + (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 - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; // Block must return empty NSData when done or nil on error and set the "error" argument accordingly
@end @end

View File

@@ -29,7 +29,8 @@
@class GCDWebUploader; @class GCDWebUploader;
@protocol GCDWebUploaderDelegate <NSObject> // These methods are always called on main thread
@protocol GCDWebUploaderDelegate <GCDWebServerDelegate>
@optional @optional
- (void)webUploader:(GCDWebUploader*)uploader didDownloadFileAtPath:(NSString*)path; - (void)webUploader:(GCDWebUploader*)uploader didDownloadFileAtPath:(NSString*)path;
- (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path; - (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path;
@@ -51,6 +52,7 @@
- (instancetype)initWithUploadDirectory:(NSString*)path; - (instancetype)initWithUploadDirectory:(NSString*)path;
@end @end
// These methods can be called from any thread
@interface GCDWebUploader (Subclassing) @interface GCDWebUploader (Subclassing)
- (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath; // Default implementation returns YES - (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath; // Default implementation returns YES
- (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; // Default implementation returns YES - (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath; // Default implementation returns YES

View File

@@ -45,7 +45,6 @@
@interface GCDWebUploader () { @interface GCDWebUploader () {
@private @private
NSString* _uploadDirectory; NSString* _uploadDirectory;
id<GCDWebUploaderDelegate> __unsafe_unretained _delegate;
NSArray* _allowedExtensions; NSArray* _allowedExtensions;
BOOL _showHidden; BOOL _showHidden;
NSString* _title; NSString* _title;
@@ -143,9 +142,9 @@
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading file name \"%@\" is not allowed", 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(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didDownloadFileAtPath:absolutePath]; [self.delegate webUploader:self didDownloadFileAtPath:absolutePath];
}); });
} }
return [GCDWebServerFileResponse responseWithFile:absolutePath isAttachment:YES]; return [GCDWebServerFileResponse responseWithFile:absolutePath isAttachment:YES];
@@ -171,9 +170,9 @@
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath]; 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(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didUploadFileAtPath:absolutePath]; [self.delegate webUploader:self didUploadFileAtPath:absolutePath];
}); });
} }
return [GCDWebServerDataResponse responseWithJSONObject:@{} contentType:contentType]; return [GCDWebServerDataResponse responseWithJSONObject:@{} contentType:contentType];
@@ -204,9 +203,9 @@
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving \"%@\" to \"%@\"", oldRelativePath, newRelativePath]; 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(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath]; [self.delegate webUploader:self didMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath];
}); });
} }
return [GCDWebServerDataResponse responseWithJSONObject:@{}]; return [GCDWebServerDataResponse responseWithJSONObject:@{}];
@@ -234,9 +233,9 @@
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath]; 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(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didDeleteItemAtPath:absolutePath]; [self.delegate webUploader:self didDeleteItemAtPath:absolutePath];
}); });
} }
return [GCDWebServerDataResponse responseWithJSONObject:@{}]; return [GCDWebServerDataResponse responseWithJSONObject:@{}];
@@ -260,9 +259,9 @@
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath]; 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(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webUploader:self didCreateDirectoryAtPath:absolutePath]; [self.delegate webUploader:self didCreateDirectoryAtPath:absolutePath];
}); });
} }
return [GCDWebServerDataResponse responseWithJSONObject:@{}]; return [GCDWebServerDataResponse responseWithJSONObject:@{}];
@@ -272,7 +271,7 @@
@implementation GCDWebUploader @implementation GCDWebUploader
@synthesize uploadDirectory=_uploadDirectory, delegate=_delegate, allowedFileExtensions=_allowedExtensions, showHiddenFiles=_showHidden, @synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, showHiddenFiles=_showHidden,
title=_title, header=_header, prologue=_prologue, epilogue=_epilogue, footer=_footer; title=_title, header=_header, prologue=_prologue, epilogue=_epilogue, footer=_footer;
- (instancetype)initWithUploadDirectory:(NSString*)path { - (instancetype)initWithUploadDirectory:(NSString*)path {

View File

@@ -39,6 +39,10 @@
#import "GCDWebUploader.h" #import "GCDWebUploader.h"
#ifndef __GCDWEBSERVER_ENABLE_TESTING__
#error __GCDWEBSERVER_ENABLE_TESTING__ must be defined
#endif
typedef enum { typedef enum {
kMode_WebServer = 0, kMode_WebServer = 0,
kMode_HTMLPage, kMode_HTMLPage,
@@ -48,6 +52,77 @@ typedef enum {
kMode_StreamingResponse kMode_StreamingResponse
} Mode; } 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)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 main(int argc, const char* argv[]) {
int result = -1; int result = -1;
@autoreleasepool { @autoreleasepool {
@@ -55,9 +130,13 @@ int main(int argc, const char* argv[]) {
BOOL recording = NO; BOOL recording = NO;
NSString* rootDirectory = NSHomeDirectory(); NSString* rootDirectory = NSHomeDirectory();
NSString* testDirectory = nil; NSString* testDirectory = nil;
NSString* authenticationMethod = nil;
NSString* authenticationRealm = nil;
NSString* authenticationUser = nil;
NSString* authenticationPassword = nil;
if (argc == 1) { 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 { } else {
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (argv[i][0] != '-') { if (argv[i][0] != '-') {
@@ -86,6 +165,18 @@ int main(int argc, const char* argv[]) {
} else if (!strcmp(argv[i], "-tests") && (i + 1 < argc)) { } else if (!strcmp(argv[i], "-tests") && (i + 1 < argc)) {
++i; ++i;
testDirectory = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:argv[i] length:strlen(argv[i])] stringByStandardizingPath]; 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]];
} }
} }
} }
@@ -195,21 +286,36 @@ int main(int argc, const char* argv[]) {
#endif #endif
if (webServer) { if (webServer) {
Delegate* delegate = [[Delegate alloc] init];
webServer.delegate = delegate;
if (testDirectory) { if (testDirectory) {
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]); 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 { } else {
if (recording) { if (recording) {
fprintf(stdout, "<RECORDING ENABLED>\n"); fprintf(stdout, "<RECORDING ENABLED>\n");
webServer.recordingEnabled = YES; webServer.recordingEnabled = YES;
} }
fprintf(stdout, "\n"); 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; result = 0;
} }
} }
#if !__has_feature(objc_arc) #if !__has_feature(objc_arc)
[webServer release]; [webServer release];
[delegate release];
#endif #endif
} }
} }

128
README.md
View File

@@ -3,8 +3,8 @@ Overview
[![Build Status](https://travis-ci.org/swisspol/GCDWebServer.svg?branch=master)](https://travis-ci.org/swisspol/GCDWebServer) [![Build Status](https://travis-ci.org/swisspol/GCDWebServer.svg?branch=master)](https://travis-ci.org/swisspol/GCDWebServer)
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: 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:
* Easy to use and understand architecture with only 4 core classes: server, connection, request and response (see GCDWebServer Architecture below) * Easy to use and understand architecture with only 4 core classes: server, connection, request and response (see "Understanding GCDWebServer's Architecture" below)
* Well designed API for easy integration and customization * 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 * Entirely built with an event-driven design using [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) for maximal performance and concurrency
* No dependencies on third-party source code * No dependencies on third-party source code
@@ -17,14 +17,12 @@ Extra built-in features:
* [Chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding) for request and response HTTP bodies * [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 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 * [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: 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 * [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) * [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"
What's not supported (but not really required from an embedded HTTP server): What's not supported (but not really required from an embedded HTTP server):
* Keep-alive connections * Keep-alive connections
@@ -37,7 +35,7 @@ Requirements:
Getting Started 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: Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Xcode project's Podfile:
``` ```
@@ -55,10 +53,12 @@ pod "GCDWebServer/WebDAV", "~> 2.0"
Hello World 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 ```objectivec
#import "GCDWebServer.h" #import "GCDWebServer.h"
#import "GCDWebServerDataResponse.h"
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
@autoreleasepool { @autoreleasepool {
@@ -66,7 +66,7 @@ int main(int argc, const char* argv[]) {
// Create server // Create server
GCDWebServer* webServer = [[GCDWebServer alloc] init]; 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" [webServer addDefaultHandlerForMethod:@"GET"
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
@@ -75,10 +75,10 @@ int main(int argc, const char* argv[]) {
}]; }];
// Use convenience method that runs server on port 8080 until SIGINT received // Use convenience method that runs server on port 8080 until SIGINT received (i.e. Ctrl-C in Terminal)
[webServer runWithPort:8080]; [webServer runWithPort:8080];
// Destroy server // Destroy server (unnecessary if using ARC)
[webServer release]; [webServer release];
} }
@@ -86,21 +86,51 @@ int main(int argc, const char* argv[]) {
} }
``` ```
**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 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 ```objectivec
#import "GCDWebUploader.h" #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 { - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
GCDWebUploader* webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath]; _webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
[webUploader start]; [_webUploader start];
NSLog(@"Visit %@ in your web browser", webUploader.serverURL); NSLog(@"Visit %@ in your web browser", _webUploader.serverURL);
return YES; return YES;
} }
``` ```
@@ -108,20 +138,22 @@ Simply instantiate and run a GCDWebUploader instance then visit http://{YOUR-IOS
WebDAV Server in iOS Apps 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). 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 ```objectivec
#import "GCDWebDAVServer.h" #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 { - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
GCDWebDAVServer* davServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:documentsPath]; _davServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:documentsPath];
[davServer start]; [_davServer start];
NSLog(@"Visit %@ in your WebDAV client", davServer.serverURL); NSLog(@"Visit %@ in your WebDAV client", _davServer.serverURL);
return YES; return YES;
} }
``` ```
@@ -129,7 +161,7 @@ Simply instantiate and run a GCDWebDAVServer instance then connect to http://{YO
Serving a Static Website 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 ```objectivec
#import "GCDWebServer.h" #import "GCDWebServer.h"
@@ -140,7 +172,7 @@ int main(int argc, const char* argv[]) {
GCDWebServer* webServer = [[GCDWebServer alloc] init]; GCDWebServer* webServer = [[GCDWebServer alloc] init];
[webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:3600 allowRangeRequests:YES]; [webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:3600 allowRangeRequests:YES];
[webServer runWithPort:8080]; [webServer runWithPort:8080];
[webServer release]; [webServer release]; // Remove if using ARC
} }
return 0; return 0;
@@ -150,20 +182,20 @@ int main(int argc, const char* argv[]) {
Using GCDWebServer 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. 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. 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's architecture consists of only 4 core classes:
* [GCDWebServer](CGDWebServer/GCDWebServer.h) manages the socket that listens for new HTTP connections and the list of handlers used by the server. * [GCDWebServer](GCDWebServer/Core/GCDWebServer.h) manages the socket that listens for new HTTP connections and the list of handlers used by the server.
* [GCDWebServerConnection](CGDWebServer/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. * [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](CGDWebServer/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](CGDWebServer/Requests) of 'GCDWebServerRequest' to handle common cases like storing the body in memory or stream it to a file on disk. * [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](CGDWebServer/GCDWebServerResponse.h) is created by the request handler and wraps the response HTTP headers and optional body. GCDWebServer comes with [several subclasses](CGDWebServer/Responses) of 'GCDWebServerResponse' to handle common cases like HTML text in memory or streaming a file from 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 Implementing Handlers
===================== =====================
@@ -171,15 +203,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__. 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: 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 ```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. * 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 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 ```objectivec
[self addHandlerForMethod:@"GET" [self addHandlerForMethod:@"GET"
@@ -197,8 +243,8 @@ Advanced Example 2: Implementing Forms
====================================== ======================================
To implement an HTTP form, you need a pair of handlers: 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 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 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 ```objectivec
[webServer addHandlerForMethod:@"GET" [webServer addHandlerForMethod:@"GET"
@@ -233,7 +279,7 @@ To implement an HTTP form, you need a pair of handlers:
Advanced Example 3: Serving a Dynamic Website 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: 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:
@@ -274,4 +320,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. 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/bash -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" CONFIGURATION="Release"
MRC_BUILD_DIR="/tmp/GCDWebServer-MRC" MRC_BUILD_DIR="/tmp/GCDWebServer-MRC"
@@ -14,17 +22,25 @@ PAYLOAD_DIR="/tmp/GCDWebServer"
function runTests { function runTests {
rm -rf "$PAYLOAD_DIR" rm -rf "$PAYLOAD_DIR"
ditto -x -k "$PAYLOAD_ZIP" "$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" logLevel=2 $1 -mode "$2" -root "$PAYLOAD_DIR/Payload" -tests "$3"
} }
# Build in manual memory management mode # Build for iOS in manual memory management mode (TODO: run tests on iOS)
rm -rf "MRC_BUILD_DIR" rm -rf "$MRC_BUILD_DIR"
xcodebuild -target "$TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null 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 # Build for iOS in ARC mode (TODO: run tests on iOS)
rm -rf "ARC_BUILD_DIR" rm -rf "$ARC_BUILD_DIR"
xcodebuild -target "$TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null 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 # Run tests
runTests $MRC_PRODUCT "webServer" "Tests/WebServer" runTests $MRC_PRODUCT "webServer" "Tests/WebServer"

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:52:42 GMT Date: Sat, 12 Apr 2014 04:52:42 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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: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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:52:47 GMT Date: Sat, 12 Apr 2014 04:52:47 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:52:47 GMT Date: Sat, 12 Apr 2014 04:52:47 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,9 +7,9 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:52:51 GMT Date: Sat, 12 Apr 2014 04:52:51 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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%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>/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:07 GMT Date: Sat, 12 Apr 2014 04:53:07 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:07 GMT Date: Sat, 12 Apr 2014 04:53:07 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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%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>/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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:13 GMT Date: Sat, 12 Apr 2014 04:53:13 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:14 GMT Date: Sat, 12 Apr 2014 04:53:14 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:14 GMT Date: Sat, 12 Apr 2014 04:53:14 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:22 GMT Date: Sat, 12 Apr 2014 04:53:22 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/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> </D:multistatus>

View File

@@ -7,9 +7,9 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:35 GMT Date: Sat, 12 Apr 2014 04:53:35 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:39 GMT Date: Sat, 12 Apr 2014 04:53:39 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:44 GMT Date: Sat, 12 Apr 2014 04:53:44 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 04:53:44 GMT Date: Sat, 12 Apr 2014 04:53:44 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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: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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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: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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:55 GMT Date: Sat, 12 Apr 2014 05:10:55 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:56 GMT Date: Sat, 12 Apr 2014 05:10:56 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:56 GMT Date: Sat, 12 Apr 2014 05:10:56 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:56 GMT Date: Sat, 12 Apr 2014 05:10:56 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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: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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:10:56 GMT Date: Sat, 12 Apr 2014 05:10:56 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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: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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:00 GMT Date: Sat, 12 Apr 2014 05:11:00 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:13 GMT Date: Sat, 12 Apr 2014 05:11:13 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:13 GMT Date: Sat, 12 Apr 2014 05:11:13 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:13 GMT Date: Sat, 12 Apr 2014 05:11:13 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:14 GMT Date: Sat, 12 Apr 2014 05:11:14 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:16 GMT Date: Sat, 12 Apr 2014 05:11:16 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:16 GMT Date: Sat, 12 Apr 2014 05:11:16 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:16 GMT Date: Sat, 12 Apr 2014 05:11:16 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:17 GMT Date: Sat, 12 Apr 2014 05:11:17 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:18 GMT Date: Sat, 12 Apr 2014 05:11:18 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:26 GMT Date: Sat, 12 Apr 2014 05:11:26 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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: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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:27 GMT Date: Sat, 12 Apr 2014 05:11:27 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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: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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:31 GMT Date: Sat, 12 Apr 2014 05:11:31 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/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>/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> </D:multistatus>

View File

@@ -7,7 +7,7 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:32 GMT Date: Sat, 12 Apr 2014 05:11:32 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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: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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:32 GMT Date: Sat, 12 Apr 2014 05:11:32 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/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>/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> </D:multistatus>

View File

@@ -7,5 +7,5 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:33 GMT Date: Sat, 12 Apr 2014 05:11:33 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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:multistatus> </D:multistatus>

View File

@@ -7,9 +7,9 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:36 GMT Date: Sat, 12 Apr 2014 05:11:36 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/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>/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:response><D:href>/untitled%20folder</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response> <D:response><D:href>/untitled%20folder</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
</D:multistatus> </D:multistatus>

View File

@@ -7,9 +7,9 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:37 GMT Date: Sat, 12 Apr 2014 05:11:37 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/Backup</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response> <D:response><D:href>/Backup</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+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>/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> </D:multistatus>

View File

@@ -7,9 +7,9 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:37 GMT Date: Sat, 12 Apr 2014 05:11:37 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/Backup</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response> <D:response><D:href>/Backup</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+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>/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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:39 GMT Date: Sat, 12 Apr 2014 05:11:39 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/Backup</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response> <D:response><D:href>/Backup</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+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-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> </D:multistatus>

View File

@@ -7,8 +7,8 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:40 GMT Date: Sat, 12 Apr 2014 05:11:40 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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>/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>/Backup</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+00:00</D:creationdate></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response> <D:response><D:href>/Backup</D:href><D:propstat><D:prop><D:resourcetype><D:collection/></D:resourcetype><D:creationdate>2014-04-12T05:11:35+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-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> </D:multistatus>

View File

@@ -7,6 +7,6 @@ Server: GCDWebDAVServer
Date: Sat, 12 Apr 2014 05:11:42 GMT Date: Sat, 12 Apr 2014 05:11:42 GMT
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"> <?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/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> </D:multistatus>

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