72 Commits

Author SHA1 Message Date
Pierre-Olivier Latour
c6d118f4ec Merge pull request #487 from gezihuzi/dev_bonjour_extent
Bonjour service support custom txt data
2020-08-17 06:45:49 -07:00
Pierre-Olivier Latour
0da9ee6afe Merge pull request #497 from trapper-/master
Replace deprecated MobileCoreServices with CoreServices
2020-08-17 06:44:20 -07:00
Regan
6c93927e12 Replace deprecated MobileCoreServices with CoreServices 2020-07-22 12:32:16 +08:00
gezihuzi
f4cf591e50 replace MobileCoreServices to CoreServices 2020-05-29 15:39:20 +08:00
gezihuzi
5c737549fc Merge remote-tracking branch 'origin/master' into dev_bonjour_extent 2020-03-30 10:00:39 +08:00
Pierre-Olivier Latour
1c36bf07c8 Bumped version 2020-03-15 09:40:36 -07:00
Pierre-Olivier Latour
df8d66f6c8 Formatted source 2020-03-15 09:35:58 -07:00
Pierre-Olivier Latour
45432e6563 Updated Travis CI to Xcode 11.3 2020-03-15 09:31:50 -07:00
Pierre-Olivier Latour
7720b1363d Ignore deprecation warning for CC_MD5 on iOS 13+ and macOS 10.15+
Fixes #427
2020-03-15 09:30:51 -07:00
Pierre-Olivier Latour
a7a6cfdbc7 Fixed Info.plist build warnings 2020-03-15 09:27:01 -07:00
Pierre-Olivier Latour
70f687e34b Fixed Clang build warning 2020-03-15 09:27:01 -07:00
Pierre-Olivier Latour
11967061a1 Updated to Xcode 11 2020-03-15 09:24:27 -07:00
gezihuzi
2d8dc8775d Support setting txtData 2019-12-14 23:02:23 +08:00
Pierre-Olivier Latour
9cb7caacbd Update README.md 2019-08-09 06:52:24 -07:00
Pierre-Olivier Latour
ac10c0c5b0 Updated Travis CI to Xcode 10.3 2019-08-09 06:50:31 -07:00
Pierre-Olivier Latour
acdb5c9262 Bumped version 2019-08-09 06:26:10 -07:00
Pierre-Olivier Latour
f54cc20bd6 Formatted source 2019-08-09 06:26:10 -07:00
Pierre-Olivier Latour
02738433bf Enforce hidden and extensions restrictions when moving and copying files in uploaders
Fixes #433
2019-08-09 06:25:37 -07:00
Pierre-Olivier Latour
c9563db0a6 Fixed GCDWebServerBodyReaderCompletionBlock not allowing null data
Fixed #434
2019-08-09 06:16:08 -07:00
Pierre-Olivier Latour
cd1eea5612 Updated to Swift 5 2019-08-09 06:08:56 -07:00
Pierre-Olivier Latour
f7c1c4eff5 Updated to Xcode 10.3 2019-08-09 06:05:19 -07:00
Pierre-Olivier Latour
fdc0feddf0 Merge pull request #426 from beazlr02/patch-1
Update swebServer.swift hello world
2019-07-03 11:46:28 +02:00
Ross Beazley
25dbee032d Update swebServer.swift hello world
looks like the example code hasn't been updated when the method signatures have changed
2019-07-03 10:43:14 +01:00
Pierre-Olivier Latour
c3c7aaad00 Revert "Merge pull request #416 from melle/app-extensions"
This reverts commit 8d0a3599ee, reversing
changes made to 7e4dd53c98.
2019-03-13 13:40:16 -07:00
Pierre-Olivier Latour
2c53064f5d Merge pull request #379 from kayak/runtime-logger
Added support to override built-in logger at runtime
2019-03-13 23:45:25 +09:00
Pierre-Olivier Latour
8d0a3599ee Merge pull request #416 from melle/app-extensions
Allow to build and run GDWebServer in app extensions.
2019-03-13 23:32:50 +09:00
Thomas Mellenthin
653dfb727b Allow to build and run GDWebServer in app extensions. 2019-03-13 15:26:52 +01:00
Pierre-Olivier Latour
7e4dd53c98 Update README.md 2019-01-06 12:25:18 -08:00
Pierre-Olivier Latour
4739d208c0 Bumped version 2019-01-06 12:10:28 -08:00
Pierre-Olivier Latour
f57c307e7d Update README.md 2019-01-06 12:07:45 -08:00
Pierre-Olivier Latour
466b1f8444 Updated copyright years 2019-01-06 12:03:46 -08:00
Pierre-Olivier Latour
fd565421dc Updated README 2019-01-06 12:03:08 -08:00
Pierre-Olivier Latour
8811d2233e Use module in test iOS and tvOS apps 2019-01-06 11:59:40 -08:00
Pierre-Olivier Latour
e561389d33 Use explicit modulemap for frameworks 2019-01-06 11:58:43 -08:00
Pierre-Olivier Latour
32cf20a1d8 Changed iOS framework target family to "Universal" 2019-01-06 11:39:05 -08:00
Pierre-Olivier Latour
3c6c2a2b5d Switch tvOS targets to manual signing 2019-01-06 11:38:30 -08:00
Pierre-Olivier Latour
2bf2dc72c5 Use GCDWebServer framework for iOS and tvOS test apps 2019-01-06 11:32:36 -08:00
Pierre-Olivier Latour
0001648879 Fall back to "CFBundleName" in GCDWebUploader footer if "CFBundleDisplayName" is not defined 2019-01-06 11:19:27 -08:00
Pierre-Olivier Latour
cbbf5483e8 Converted tvOS test app to Swift 2019-01-06 11:16:27 -08:00
Pierre-Olivier Latour
edd1f2850b Converted iOS test app to Swift 2019-01-06 11:16:27 -08:00
Pierre-Olivier Latour
5eb5e14b70 Removed unused build setting 2019-01-06 10:31:16 -08:00
Pierre-Olivier Latour
3e46e12648 Bumped version number for framework 2019-01-06 10:30:45 -08:00
Pierre-Olivier Latour
ee4395e67d Fixed bug in _CreateHTTPMessageFromPerformingRequest() 2019-01-06 09:40:43 -08:00
Pierre-Olivier Latour
34884f273a Bumped version 2019-01-05 12:00:06 -08:00
Pierre-Olivier Latour
87745c0fde Use GCDWebServerNormalizePath() on all relative paths passed to GCDWebServer 2019-01-04 18:59:57 -08:00
Pierre-Olivier Latour
ec800b43d5 Added GCDWebServerNormalizePath() API 2019-01-04 18:58:43 -08:00
Pierre-Olivier Latour
79d9fb389c Removed -stringByStandardizingPath from path arguments to Mac CLT 2019-01-04 18:58:27 -08:00
Pierre-Olivier Latour
33d14f22e0 Use xcpretty in Run-Tests.sh if available 2019-01-03 17:35:46 -08:00
Pierre-Olivier Latour
b060305d6d Only fallback to -[NSData base64Encoding] on macOS prior to 10.9 2019-01-03 17:35:35 -08:00
Pierre-Olivier Latour
561f56e7fb Only fallback to -[NSData base64Encoding] on macOS 2019-01-03 17:30:21 -08:00
Pierre-Olivier Latour
e9fdd19830 Use @available() to check for API availability instead of -respondsToSelector: 2019-01-03 17:24:31 -08:00
Pierre-Olivier Latour
03fae468d1 Fixed implicit-retain-self warnings 2019-01-03 17:15:02 -08:00
Pierre-Olivier Latour
0a7d185417 Fixed warning 2019-01-03 17:15:02 -08:00
Pierre-Olivier Latour
1e29a0195b Fixed strict-prototypes warning 2019-01-03 17:15:02 -08:00
Pierre-Olivier Latour
4e29da53a2 Bumped version 2019-01-03 16:26:01 -08:00
Pierre-Olivier Latour
acc54ceac3 Handle CFHTTPMessageCopyBody() now returning NULL for valid messages without a body 2019-01-03 16:12:30 -08:00
Pierre-Olivier Latour
c46a2ddb22 Ensure directories are always listed in deterministic order
macOS APIs have recently changed and do not return sorted entries for directories.
2019-01-03 16:12:30 -08:00
Pierre-Olivier Latour
71e972084c Adding missing newlines to fprintf() 2019-01-03 15:58:54 -08:00
Pierre-Olivier Latour
e8c872b286 Use local "build" directory for Run-Tests.sh 2019-01-03 15:58:43 -08:00
Pierre-Olivier Latour
fc928d0e2b Improved errors detection in Run-Tests.sh 2019-01-03 15:58:06 -08:00
Pierre-Olivier Latour
e65f0eeaf1 Updated Travis to use Xcode 10.1 2019-01-03 09:20:43 -08:00
Pierre-Olivier Latour
79ae63a4c0 Fixed build warnings 2018-12-14 07:37:35 -08:00
Pierre-Olivier Latour
21cc6bfb35 Added types to collections 2018-12-14 07:37:28 -08:00
Pierre-Olivier Latour
faf28fe0f9 Updated to Xcode 10.10 2018-12-14 07:34:21 -08:00
Pierre-Olivier Latour
bac5b680df Updated to clang-format 7.0.0 2018-12-14 07:10:03 -08:00
Pierre-Olivier Latour
7df465336e Update README.md
Fixes #392
2018-10-09 06:00:52 -07:00
Duncan Cunningham
11254331d1 Make changes based on PR comments 2018-07-13 16:17:23 +02:00
Duncan Cunningham
9f345c6858 Added support to override built-in logger at runtime 2018-05-29 13:21:40 +02:00
Pierre-Olivier Latour
a554893844 Bumped version 2017-11-02 14:01:02 -07:00
Pierre-Olivier Latour
06569fe2cc Fixed NS_ASSUME_NONNULL_BEGIN not located at the right place in GCDWebServerPrivate.h 2017-11-02 14:00:10 -07:00
Pierre-Olivier Latour
e812d3f43c Updated to Xcode 9.1 2017-11-02 13:59:09 -07:00
Johnny Wu
d30c88729d Simplify podspec (#334) 2017-08-23 11:20:54 +02:00
67 changed files with 897 additions and 1085 deletions

5
.gitignore vendored
View File

@@ -1,6 +1,5 @@
.DS_Store .DS_Store
xcuserdata xcuserdata
project.xcworkspace project.xcworkspace
/build
Tests/Payload /Carthage/Build
Carthage/Build

View File

@@ -1,3 +1,3 @@
language: objective-c language: objective-c
script: ./Run-Tests.sh script: ./Run-Tests.sh
osx_image: xcode8.2 osx_image: xcode11.3

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -26,27 +26,27 @@
*/ */
// GCDWebServer Core // GCDWebServer Core
#import <GCDWebServers/GCDWebServer.h> #import "GCDWebServer.h"
#import <GCDWebServers/GCDWebServerConnection.h> #import "GCDWebServerConnection.h"
#import <GCDWebServers/GCDWebServerFunctions.h> #import "GCDWebServerFunctions.h"
#import <GCDWebServers/GCDWebServerHTTPStatusCodes.h> #import "GCDWebServerHTTPStatusCodes.h"
#import <GCDWebServers/GCDWebServerResponse.h> #import "GCDWebServerResponse.h"
#import <GCDWebServers/GCDWebServerRequest.h> #import "GCDWebServerRequest.h"
// GCDWebServer Requests // GCDWebServer Requests
#import <GCDWebServers/GCDWebServerDataRequest.h> #import "GCDWebServerDataRequest.h"
#import <GCDWebServers/GCDWebServerFileRequest.h> #import "GCDWebServerFileRequest.h"
#import <GCDWebServers/GCDWebServerMultiPartFormRequest.h> #import "GCDWebServerMultiPartFormRequest.h"
#import <GCDWebServers/GCDWebServerURLEncodedFormRequest.h> #import "GCDWebServerURLEncodedFormRequest.h"
// GCDWebServer Responses // GCDWebServer Responses
#import <GCDWebServers/GCDWebServerDataResponse.h> #import "GCDWebServerDataResponse.h"
#import <GCDWebServers/GCDWebServerErrorResponse.h> #import "GCDWebServerErrorResponse.h"
#import <GCDWebServers/GCDWebServerFileResponse.h> #import "GCDWebServerFileResponse.h"
#import <GCDWebServers/GCDWebServerStreamedResponse.h> #import "GCDWebServerStreamedResponse.h"
// GCDWebUploader // GCDWebUploader
#import <GCDWebServers/GCDWebUploader.h> #import "GCDWebUploader.h"
// GCDWebDAVServer // GCDWebDAVServer
#import <GCDWebServers/GCDWebDAVServer.h> #import "GCDWebDAVServer.h"

View File

@@ -1,6 +1,8 @@
#import <GCDWebServers/GCDWebServers.h> #import <GCDWebServers/GCDWebServers.h>
#import <XCTest/XCTest.h> #import <XCTest/XCTest.h>
#pragma clang diagnostic ignored "-Weverything" // Prevent "messaging to unqualified id" warnings
@interface Tests : XCTestCase @interface Tests : XCTestCase
@end @end
@@ -21,4 +23,21 @@
XCTAssertNotNil(server); XCTAssertNotNil(server);
} }
- (void)testPaths {
XCTAssertEqualObjects(GCDWebServerNormalizePath(@""), @"");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"/foo/"), @"/foo");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"foo/bar"), @"foo/bar");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"foo//bar"), @"foo/bar");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"foo/bar//"), @"foo/bar");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"foo/./bar"), @"foo/bar");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"foo/bar/."), @"foo/bar");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"foo/../bar"), @"bar");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"/foo/../bar"), @"/bar");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"/foo/.."), @"/");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"/.."), @"/");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"."), @"");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@".."), @"");
XCTAssertEqualObjects(GCDWebServerNormalizePath(@"../.."), @"");
}
@end @end

View File

@@ -0,0 +1,3 @@
framework module GCDWebServers {
umbrella header "GCDWebServers.h"
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -95,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN
* *
* The default value is nil i.e. all file extensions are allowed. * The default value is nil i.e. all file extensions are allowed.
*/ */
@property(nonatomic, copy) NSArray* allowedFileExtensions; @property(nonatomic, copy) NSArray<NSString*>* allowedFileExtensions;
/** /**
* Sets if files and directories whose name start with a period are allowed to * Sets if files and directories whose name start with a period are allowed to

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -77,7 +77,7 @@ NS_ASSUME_NONNULL_END
- (instancetype)initWithUploadDirectory:(NSString*)path { - (instancetype)initWithUploadDirectory:(NSString*)path {
if ((self = [super init])) { if ((self = [super init])) {
_uploadDirectory = [[path stringByStandardizingPath] copy]; _uploadDirectory = [path copy];
GCDWebDAVServer* __unsafe_unretained server = self; GCDWebDAVServer* __unsafe_unretained server = self;
// 9.1 PROPFIND method // 9.1 PROPFIND method
@@ -157,11 +157,6 @@ NS_ASSUME_NONNULL_END
@implementation GCDWebDAVServer (Methods) @implementation GCDWebDAVServer (Methods)
// Must match implementation in GCDWebUploader
- (BOOL)_checkSandboxedPath:(NSString*)path {
return [[path stringByStandardizingPath] hasPrefix:_uploadDirectory];
}
- (BOOL)_checkFileExtension:(NSString*)fileName { - (BOOL)_checkFileExtension:(NSString*)fileName {
if (_allowedFileExtensions && ![_allowedFileExtensions containsObject:[[fileName pathExtension] lowercaseString]]) { if (_allowedFileExtensions && ![_allowedFileExtensions containsObject:[[fileName pathExtension] lowercaseString]]) {
return NO; return NO;
@@ -186,9 +181,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
- (GCDWebServerResponse*)performGET:(GCDWebServerRequest*)request { - (GCDWebServerResponse*)performGET:(GCDWebServerRequest*)request {
NSString* relativePath = request.path; NSString* relativePath = request.path;
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
} }
@@ -221,10 +216,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
} }
NSString* relativePath = request.path; NSString* relativePath = request.path;
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
if (![self _checkSandboxedPath:absolutePath]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
}
BOOL isDirectory; BOOL isDirectory;
if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) { if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath];
@@ -265,9 +257,9 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
} }
NSString* relativePath = request.path; NSString* relativePath = request.path;
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
} }
@@ -299,10 +291,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
} }
NSString* relativePath = request.path; NSString* relativePath = request.path;
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
if (![self _checkSandboxedPath:absolutePath]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
}
BOOL isDirectory; BOOL isDirectory;
if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) { if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath];
@@ -348,10 +337,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
} }
NSString* srcRelativePath = request.path; NSString* srcRelativePath = request.path;
NSString* srcAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:srcRelativePath]; NSString* srcAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(srcRelativePath)];
if (![self _checkSandboxedPath:srcAbsolutePath]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
}
NSString* dstRelativePath = [request.headers objectForKey:@"Destination"]; NSString* dstRelativePath = [request.headers objectForKey:@"Destination"];
NSRange range = [dstRelativePath rangeOfString:(NSString*)[request.headers objectForKey:@"Host"]]; NSRange range = [dstRelativePath rangeOfString:(NSString*)[request.headers objectForKey:@"Host"]];
@@ -362,8 +348,8 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
#pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wdeprecated-declarations"
dstRelativePath = [[dstRelativePath substringFromIndex:(range.location + range.length)] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; dstRelativePath = [[dstRelativePath substringFromIndex:(range.location + range.length)] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
#pragma clang diagnostic pop #pragma clang diagnostic pop
NSString* dstAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:dstRelativePath]; NSString* dstAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(dstRelativePath)];
if (![self _checkSandboxedPath:dstAbsolutePath]) { if (!dstAbsolutePath) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
} }
@@ -372,9 +358,14 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Invalid destination \"%@\"", dstRelativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Invalid destination \"%@\"", dstRelativePath];
} }
NSString* itemName = [dstAbsolutePath lastPathComponent]; NSString* srcName = [srcAbsolutePath lastPathComponent];
if ((!_allowHiddenItems && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) { if ((!_allowHiddenItems && [srcName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:srcName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"%@ to item name \"%@\" is not allowed", isMove ? @"Moving" : @"Copying", itemName]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"%@ from item name \"%@\" is not allowed", isMove ? @"Moving" : @"Copying", srcName];
}
NSString* dstName = [dstAbsolutePath lastPathComponent];
if ((!_allowHiddenItems && [dstName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:dstName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"%@ to item name \"%@\" is not allowed", isMove ? @"Moving" : @"Copying", dstName];
} }
NSString* overwriteHeader = [request.headers objectForKey:@"Overwrite"]; NSString* overwriteHeader = [request.headers objectForKey:@"Overwrite"];
@@ -532,9 +523,9 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
} }
NSString* relativePath = request.path; NSString* relativePath = request.path;
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
} }
@@ -546,7 +537,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
NSArray* items = nil; NSArray* items = nil;
if (isDirectory) { if (isDirectory) {
NSError* error = nil; NSError* error = nil;
items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error]; items = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
if (items == nil) { if (items == nil) {
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath]; return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
} }
@@ -582,9 +573,9 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
} }
NSString* relativePath = request.path; NSString* relativePath = request.path;
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
} }
@@ -679,9 +670,9 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
} }
NSString* relativePath = request.path; NSString* relativePath = request.path;
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
} }

View File

@@ -7,7 +7,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'GCDWebServer' s.name = 'GCDWebServer'
s.version = '3.4.1' s.version = '3.5.4'
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'
@@ -26,31 +26,27 @@ Pod::Spec.new do |s|
cs.private_header_files = "GCDWebServer/Core/GCDWebServerPrivate.h" cs.private_header_files = "GCDWebServer/Core/GCDWebServerPrivate.h"
cs.requires_arc = true cs.requires_arc = true
cs.ios.library = 'z' cs.ios.library = 'z'
cs.ios.frameworks = 'MobileCoreServices', 'CFNetwork' cs.ios.frameworks = 'CoreServices', 'CFNetwork'
cs.tvos.library = 'z' cs.tvos.library = 'z'
cs.tvos.frameworks = 'MobileCoreServices', 'CFNetwork' cs.tvos.frameworks = 'CoreServices', 'CFNetwork'
cs.osx.library = 'z' cs.osx.library = 'z'
cs.osx.framework = 'SystemConfiguration' cs.osx.framework = 'SystemConfiguration'
end end
s.subspec 'WebDAV' do |cs| s.subspec 'WebDAV' do |cs|
cs.subspec "Core" do |ccs| cs.dependency 'GCDWebServer/Core'
ccs.dependency 'GCDWebServer/Core' cs.source_files = 'GCDWebDAVServer/*.{h,m}'
ccs.source_files = 'GCDWebDAVServer/*.{h,m}' cs.requires_arc = true
ccs.requires_arc = true cs.ios.library = 'xml2'
ccs.ios.library = 'xml2' cs.tvos.library = 'xml2'
ccs.tvos.library = 'xml2' cs.osx.library = 'xml2'
ccs.osx.library = 'xml2' cs.compiler_flags = '-I$(SDKROOT)/usr/include/libxml2'
ccs.compiler_flags = '-I$(SDKROOT)/usr/include/libxml2'
end
end end
s.subspec 'WebUploader' do |cs| s.subspec 'WebUploader' do |cs|
cs.subspec "Core" do |ccs| cs.dependency 'GCDWebServer/Core'
ccs.dependency 'GCDWebServer/Core' cs.source_files = 'GCDWebUploader/*.{h,m}'
ccs.source_files = 'GCDWebUploader/*.{h,m}' cs.requires_arc = true
ccs.requires_arc = true cs.resource = "GCDWebUploader/GCDWebUploader.bundle"
ccs.resource = "GCDWebUploader/GCDWebUploader.bundle"
end
end end
end end

View File

@@ -96,7 +96,7 @@
CEE28D501AE0098600F4023C /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2BE851018E79DAF0061360B /* SystemConfiguration.framework */; }; CEE28D501AE0098600F4023C /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2BE851018E79DAF0061360B /* SystemConfiguration.framework */; };
CEE28D511AE0098C00F4023C /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D1B2167BB17E00500836 /* CoreServices.framework */; }; CEE28D511AE0098C00F4023C /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D1B2167BB17E00500836 /* CoreServices.framework */; };
CEE28D521AE00A7A00F4023C /* GCDWebServers.h in Headers */ = {isa = PBXBuildFile; fileRef = CEE28CF31AE0051F00F4023C /* GCDWebServers.h */; settings = {ATTRIBUTES = (Public, ); }; }; CEE28D521AE00A7A00F4023C /* GCDWebServers.h in Headers */ = {isa = PBXBuildFile; fileRef = CEE28CF31AE0051F00F4023C /* GCDWebServers.h */; settings = {ATTRIBUTES = (Public, ); }; };
CEE28D571AE00AFE00F4023C /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */; }; CEE28D571AE00AFE00F4023C /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* CoreServices.framework */; };
CEE28D591AE00AFE00F4023C /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E22112981690B7AA0048D2B2 /* CFNetwork.framework */; }; CEE28D591AE00AFE00F4023C /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E22112981690B7AA0048D2B2 /* CFNetwork.framework */; };
CEE28D6A1AE1ABAA00F4023C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE28D691AE1ABAA00F4023C /* UIKit.framework */; }; CEE28D6A1AE1ABAA00F4023C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE28D691AE1ABAA00F4023C /* UIKit.framework */; };
E208D149167B76B700500836 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D148167B76B700500836 /* CFNetwork.framework */; }; E208D149167B76B700500836 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D148167B76B700500836 /* CFNetwork.framework */; };
@@ -104,6 +104,10 @@
E221128F1690B6470048D2B2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E221128E1690B6470048D2B2 /* main.m */; }; E221128F1690B6470048D2B2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E221128E1690B6470048D2B2 /* main.m */; };
E240392B1BA09207000B7089 /* GCDWebServers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE28CD11AE004D800F4023C /* GCDWebServers.framework */; }; E240392B1BA09207000B7089 /* GCDWebServers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE28CD11AE004D800F4023C /* GCDWebServers.framework */; };
E24039321BA092B7000B7089 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E24039311BA092B7000B7089 /* Tests.m */; }; E24039321BA092B7000B7089 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E24039311BA092B7000B7089 /* Tests.m */; };
E24A3C0721E2879F00C58878 /* GCDWebServers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE28CEF1AE0051F00F4023C /* GCDWebServers.framework */; };
E24A3C0821E287A300C58878 /* GCDWebServers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD18B1BE69404002CE867 /* GCDWebServers.framework */; };
E24A3C0E21E28D3C00C58878 /* GCDWebServers.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CEE28CEF1AE0051F00F4023C /* GCDWebServers.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
E24A3C0F21E28EFB00C58878 /* GCDWebServers.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD18B1BE69404002CE867 /* GCDWebServers.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
E28BAE3418F99C810095C089 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1718F99C810095C089 /* GCDWebServer.m */; }; E28BAE3418F99C810095C089 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1718F99C810095C089 /* GCDWebServer.m */; };
E28BAE3618F99C810095C089 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1918F99C810095C089 /* GCDWebServerConnection.m */; }; E28BAE3618F99C810095C089 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1918F99C810095C089 /* GCDWebServerConnection.m */; };
E28BAE3818F99C810095C089 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1B18F99C810095C089 /* GCDWebServerFunctions.m */; }; E28BAE3818F99C810095C089 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1B18F99C810095C089 /* GCDWebServerFunctions.m */; };
@@ -157,63 +161,19 @@
E2DDD1B71BE6951A002CE867 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1B61BE6951A002CE867 /* CFNetwork.framework */; }; E2DDD1B71BE6951A002CE867 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1B61BE6951A002CE867 /* CFNetwork.framework */; };
E2DDD1BA1BE69545002CE867 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1B91BE69545002CE867 /* libz.tbd */; }; E2DDD1BA1BE69545002CE867 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1B91BE69545002CE867 /* libz.tbd */; };
E2DDD1BC1BE69551002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BB1BE69551002CE867 /* libxml2.tbd */; }; E2DDD1BC1BE69551002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BB1BE69551002CE867 /* libxml2.tbd */; };
E2DDD1BE1BE6956F002CE867 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BD1BE6956F002CE867 /* MobileCoreServices.framework */; }; E2DDD1BE1BE6956F002CE867 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BD1BE6956F002CE867 /* CoreServices.framework */; };
E2DDD1C01BE69576002CE867 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BF1BE69576002CE867 /* UIKit.framework */; }; E2DDD1C01BE69576002CE867 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BF1BE69576002CE867 /* UIKit.framework */; };
E2DDD1CB1BE698A8002CE867 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1CA1BE698A8002CE867 /* main.m */; }; E2DDD1CE1BE698A8002CE867 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1CD1BE698A8002CE867 /* AppDelegate.swift */; };
E2DDD1CE1BE698A8002CE867 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1CD1BE698A8002CE867 /* AppDelegate.m */; }; E2DDD1D11BE698A8002CE867 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1D01BE698A8002CE867 /* ViewController.swift */; };
E2DDD1D11BE698A8002CE867 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1D01BE698A8002CE867 /* ViewController.m */; };
E2DDD1D41BE698A8002CE867 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD1D21BE698A8002CE867 /* Main.storyboard */; }; E2DDD1D41BE698A8002CE867 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD1D21BE698A8002CE867 /* Main.storyboard */; };
E2DDD1D61BE698A8002CE867 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD1D51BE698A8002CE867 /* Assets.xcassets */; }; E2DDD1D61BE698A8002CE867 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD1D51BE698A8002CE867 /* Assets.xcassets */; };
E2DDD1DD1BE69B1C002CE867 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BF1BE69576002CE867 /* UIKit.framework */; }; E2DDD1FD1BE69EE5002CE867 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1FC1BE69EE5002CE867 /* AppDelegate.swift */; };
E2DDD1DE1BE69BB7002CE867 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1718F99C810095C089 /* GCDWebServer.m */; }; E2DDD2001BE69EE5002CE867 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1FF1BE69EE5002CE867 /* ViewController.swift */; };
E2DDD1DF1BE69BB7002CE867 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1918F99C810095C089 /* GCDWebServerConnection.m */; };
E2DDD1E01BE69BB7002CE867 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1B18F99C810095C089 /* GCDWebServerFunctions.m */; };
E2DDD1E11BE69BB7002CE867 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1F18F99C810095C089 /* GCDWebServerRequest.m */; };
E2DDD1E21BE69BB7002CE867 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2118F99C810095C089 /* GCDWebServerResponse.m */; };
E2DDD1E31BE69BB7002CE867 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2418F99C810095C089 /* GCDWebServerDataRequest.m */; };
E2DDD1E41BE69BB7002CE867 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2618F99C810095C089 /* GCDWebServerFileRequest.m */; };
E2DDD1E51BE69BB7002CE867 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2818F99C810095C089 /* GCDWebServerMultiPartFormRequest.m */; };
E2DDD1E61BE69BB7002CE867 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2A18F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m */; };
E2DDD1E71BE69BB7002CE867 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2D18F99C810095C089 /* GCDWebServerDataResponse.m */; };
E2DDD1E81BE69BB7002CE867 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2F18F99C810095C089 /* GCDWebServerErrorResponse.m */; };
E2DDD1E91BE69BB7002CE867 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE3118F99C810095C089 /* GCDWebServerFileResponse.m */; };
E2DDD1EA1BE69BB7002CE867 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE3318F99C810095C089 /* GCDWebServerStreamedResponse.m */; };
E2DDD1EB1BE69BB7002CE867 /* GCDWebDAVServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80918F3432600C580B1 /* GCDWebDAVServer.m */; };
E2DDD1EC1BE69BB7002CE867 /* GCDWebUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = E2BE850918E77ECA0061360B /* GCDWebUploader.m */; };
E2DDD1ED1BE69BC5002CE867 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BD1BE6956F002CE867 /* MobileCoreServices.framework */; };
E2DDD1EE1BE69BC5002CE867 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1B61BE6951A002CE867 /* CFNetwork.framework */; };
E2DDD1EF1BE69BC5002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1BB1BE69551002CE867 /* libxml2.tbd */; };
E2DDD1F01BE69BC5002CE867 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD1B91BE69545002CE867 /* libz.tbd */; };
E2DDD1F11BE69BE9002CE867 /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */; };
E2DDD1FA1BE69EE5002CE867 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1F91BE69EE5002CE867 /* main.m */; };
E2DDD1FD1BE69EE5002CE867 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1FC1BE69EE5002CE867 /* AppDelegate.m */; };
E2DDD2001BE69EE5002CE867 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E2DDD1FF1BE69EE5002CE867 /* ViewController.m */; };
E2DDD2031BE69EE5002CE867 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD2011BE69EE5002CE867 /* Main.storyboard */; }; E2DDD2031BE69EE5002CE867 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD2011BE69EE5002CE867 /* Main.storyboard */; };
E2DDD2051BE69EE5002CE867 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD2041BE69EE5002CE867 /* Assets.xcassets */; }; E2DDD2051BE69EE5002CE867 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD2041BE69EE5002CE867 /* Assets.xcassets */; };
E2DDD2081BE69EE5002CE867 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD2061BE69EE5002CE867 /* LaunchScreen.storyboard */; }; E2DDD2081BE69EE5002CE867 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E2DDD2061BE69EE5002CE867 /* LaunchScreen.storyboard */; };
E2DDD20F1BE69F03002CE867 /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */; };
E2DDD2101BE69F17002CE867 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1718F99C810095C089 /* GCDWebServer.m */; };
E2DDD2111BE69F17002CE867 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1918F99C810095C089 /* GCDWebServerConnection.m */; };
E2DDD2121BE69F17002CE867 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1B18F99C810095C089 /* GCDWebServerFunctions.m */; };
E2DDD2131BE69F17002CE867 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE1F18F99C810095C089 /* GCDWebServerRequest.m */; };
E2DDD2141BE69F17002CE867 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2118F99C810095C089 /* GCDWebServerResponse.m */; };
E2DDD2151BE69F17002CE867 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2418F99C810095C089 /* GCDWebServerDataRequest.m */; };
E2DDD2161BE69F17002CE867 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2618F99C810095C089 /* GCDWebServerFileRequest.m */; };
E2DDD2171BE69F17002CE867 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2818F99C810095C089 /* GCDWebServerMultiPartFormRequest.m */; };
E2DDD2181BE69F17002CE867 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2A18F99C810095C089 /* GCDWebServerURLEncodedFormRequest.m */; };
E2DDD2191BE69F17002CE867 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2D18F99C810095C089 /* GCDWebServerDataResponse.m */; };
E2DDD21A1BE69F17002CE867 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE2F18F99C810095C089 /* GCDWebServerErrorResponse.m */; };
E2DDD21B1BE69F17002CE867 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE3118F99C810095C089 /* GCDWebServerFileResponse.m */; };
E2DDD21C1BE69F17002CE867 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E28BAE3318F99C810095C089 /* GCDWebServerStreamedResponse.m */; };
E2DDD21D1BE69F25002CE867 /* GCDWebDAVServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80918F3432600C580B1 /* GCDWebDAVServer.m */; };
E2DDD21E1BE69F25002CE867 /* GCDWebUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = E2BE850918E77ECA0061360B /* GCDWebUploader.m */; };
E2DDD21F1BE6A061002CE867 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE28D691AE1ABAA00F4023C /* UIKit.framework */; };
E2DDD2201BE6A067002CE867 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */; };
E2DDD2211BE6A06E002CE867 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E22112981690B7AA0048D2B2 /* CFNetwork.framework */; };
E2DDD2251BE6A0AE002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD2241BE6A0AE002CE867 /* libxml2.tbd */; }; E2DDD2251BE6A0AE002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD2241BE6A0AE002CE867 /* libxml2.tbd */; };
E2DDD2271BE6A0B4002CE867 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD2261BE6A0B4002CE867 /* libz.tbd */; }; E2DDD2271BE6A0B4002CE867 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD2261BE6A0B4002CE867 /* libz.tbd */; };
E2DDD2281BE6A0D8002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD2241BE6A0AE002CE867 /* libxml2.tbd */; };
E2DDD2291BE6A0D8002CE867 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD2261BE6A0B4002CE867 /* libz.tbd */; };
E2DDD22B1BE6A0EB002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD22A1BE6A0EB002CE867 /* libxml2.tbd */; }; E2DDD22B1BE6A0EB002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD22A1BE6A0EB002CE867 /* libxml2.tbd */; };
E2DDD22D1BE6A0EF002CE867 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD22C1BE6A0EF002CE867 /* libz.tbd */; }; E2DDD22D1BE6A0EF002CE867 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD22C1BE6A0EF002CE867 /* libz.tbd */; };
E2DDD22E1BE6A106002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD22A1BE6A0EB002CE867 /* libxml2.tbd */; }; E2DDD22E1BE6A106002CE867 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E2DDD22A1BE6A0EB002CE867 /* libxml2.tbd */; };
@@ -242,6 +202,20 @@
remoteGlobalIDString = CEE28CD01AE004D800F4023C; remoteGlobalIDString = CEE28CD01AE004D800F4023C;
remoteInfo = "GCDWebServers (Mac)"; remoteInfo = "GCDWebServers (Mac)";
}; };
E24A3C0321E2879000C58878 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
proxyType = 1;
remoteGlobalIDString = CEE28CEE1AE0051F00F4023C;
remoteInfo = "GCDWebServers (iOS)";
};
E24A3C0521E2879700C58878 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
proxyType = 1;
remoteGlobalIDString = E2DDD18A1BE69404002CE867;
remoteInfo = "GCDWebServers (tvOS)";
};
E274F87A187E77E3009E0582 /* PBXContainerItemProxy */ = { E274F87A187E77E3009E0582 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
@@ -273,6 +247,28 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
E24A3C0C21E28D1E00C58878 /* Copy Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
E24A3C0E21E28D3C00C58878 /* GCDWebServers.framework in Copy Frameworks */,
);
name = "Copy Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
E24A3C0D21E28D2300C58878 /* Copy Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
E24A3C0F21E28EFB00C58878 /* GCDWebServers.framework in Copy Frameworks */,
);
name = "Copy Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
E2BE850E18E788910061360B /* CopyFiles */ = { E2BE850E18E788910061360B /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -297,9 +293,10 @@
E208D1B2167BB17E00500836 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; E208D1B2167BB17E00500836 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
E221128E1690B6470048D2B2 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; E221128E1690B6470048D2B2 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
E22112981690B7AA0048D2B2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; E22112981690B7AA0048D2B2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; }; E221129C1690B7BA0048D2B2 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CoreServices.framework; sourceTree = DEVELOPER_DIR; };
E24039251BA09207000B7089 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E24039251BA09207000B7089 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
E24039311BA092B7000B7089 /* Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = "<group>"; }; E24039311BA092B7000B7089 /* Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = "<group>"; };
E24A3C4021E2940600C58878 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
E28BAE1618F99C810095C089 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; }; E28BAE1618F99C810095C089 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
E28BAE1718F99C810095C089 /* GCDWebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServer.m; sourceTree = "<group>"; }; E28BAE1718F99C810095C089 /* GCDWebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServer.m; sourceTree = "<group>"; };
E28BAE1818F99C810095C089 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerConnection.h; sourceTree = "<group>"; }; E28BAE1818F99C810095C089 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerConnection.h; sourceTree = "<group>"; };
@@ -338,23 +335,17 @@
E2DDD1B61BE6951A002CE867 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; E2DDD1B61BE6951A002CE867 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
E2DDD1B91BE69545002CE867 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; }; E2DDD1B91BE69545002CE867 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
E2DDD1BB1BE69551002CE867 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; }; E2DDD1BB1BE69551002CE867 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; };
E2DDD1BD1BE6956F002CE867 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; }; E2DDD1BD1BE6956F002CE867 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/CoreServices.framework; sourceTree = DEVELOPER_DIR; };
E2DDD1BF1BE69576002CE867 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; E2DDD1BF1BE69576002CE867 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
E2DDD1C71BE698A8002CE867 /* GCDWebServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GCDWebServer.app; sourceTree = BUILT_PRODUCTS_DIR; }; E2DDD1C71BE698A8002CE867 /* GCDWebServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GCDWebServer.app; sourceTree = BUILT_PRODUCTS_DIR; };
E2DDD1CA1BE698A8002CE867 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; E2DDD1CD1BE698A8002CE867 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
E2DDD1CC1BE698A8002CE867 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; E2DDD1D01BE698A8002CE867 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
E2DDD1CD1BE698A8002CE867 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
E2DDD1CF1BE698A8002CE867 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
E2DDD1D01BE698A8002CE867 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
E2DDD1D31BE698A8002CE867 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; E2DDD1D31BE698A8002CE867 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
E2DDD1D51BE698A8002CE867 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; E2DDD1D51BE698A8002CE867 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E2DDD1D71BE698A8002CE867 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; E2DDD1D71BE698A8002CE867 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E2DDD1F61BE69EE4002CE867 /* GCDWebServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GCDWebServer.app; sourceTree = BUILT_PRODUCTS_DIR; }; E2DDD1F61BE69EE4002CE867 /* GCDWebServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GCDWebServer.app; sourceTree = BUILT_PRODUCTS_DIR; };
E2DDD1F91BE69EE5002CE867 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; E2DDD1FC1BE69EE5002CE867 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
E2DDD1FB1BE69EE5002CE867 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; E2DDD1FF1BE69EE5002CE867 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
E2DDD1FC1BE69EE5002CE867 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
E2DDD1FE1BE69EE5002CE867 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
E2DDD1FF1BE69EE5002CE867 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
E2DDD2021BE69EE5002CE867 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; E2DDD2021BE69EE5002CE867 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
E2DDD2041BE69EE5002CE867 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; E2DDD2041BE69EE5002CE867 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E2DDD2071BE69EE5002CE867 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; E2DDD2071BE69EE5002CE867 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
@@ -396,7 +387,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
CEE28D6A1AE1ABAA00F4023C /* UIKit.framework in Frameworks */, CEE28D6A1AE1ABAA00F4023C /* UIKit.framework in Frameworks */,
CEE28D571AE00AFE00F4023C /* MobileCoreServices.framework in Frameworks */, CEE28D571AE00AFE00F4023C /* CoreServices.framework in Frameworks */,
CEE28D591AE00AFE00F4023C /* CFNetwork.framework in Frameworks */, CEE28D591AE00AFE00F4023C /* CFNetwork.framework in Frameworks */,
E2DDD2251BE6A0AE002CE867 /* libxml2.tbd in Frameworks */, E2DDD2251BE6A0AE002CE867 /* libxml2.tbd in Frameworks */,
E2DDD2271BE6A0B4002CE867 /* libz.tbd in Frameworks */, E2DDD2271BE6A0B4002CE867 /* libz.tbd in Frameworks */,
@@ -416,7 +407,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E2DDD1C01BE69576002CE867 /* UIKit.framework in Frameworks */, E2DDD1C01BE69576002CE867 /* UIKit.framework in Frameworks */,
E2DDD1BE1BE6956F002CE867 /* MobileCoreServices.framework in Frameworks */, E2DDD1BE1BE6956F002CE867 /* CoreServices.framework in Frameworks */,
E2DDD1B71BE6951A002CE867 /* CFNetwork.framework in Frameworks */, E2DDD1B71BE6951A002CE867 /* CFNetwork.framework in Frameworks */,
E2DDD1BC1BE69551002CE867 /* libxml2.tbd in Frameworks */, E2DDD1BC1BE69551002CE867 /* libxml2.tbd in Frameworks */,
E2DDD1BA1BE69545002CE867 /* libz.tbd in Frameworks */, E2DDD1BA1BE69545002CE867 /* libz.tbd in Frameworks */,
@@ -427,11 +418,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E2DDD1DD1BE69B1C002CE867 /* UIKit.framework in Frameworks */, E24A3C0821E287A300C58878 /* GCDWebServers.framework in Frameworks */,
E2DDD1ED1BE69BC5002CE867 /* MobileCoreServices.framework in Frameworks */,
E2DDD1EE1BE69BC5002CE867 /* CFNetwork.framework in Frameworks */,
E2DDD1EF1BE69BC5002CE867 /* libxml2.tbd in Frameworks */,
E2DDD1F01BE69BC5002CE867 /* libz.tbd in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -439,11 +426,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E2DDD21F1BE6A061002CE867 /* UIKit.framework in Frameworks */, E24A3C0721E2879F00C58878 /* GCDWebServers.framework in Frameworks */,
E2DDD2201BE6A067002CE867 /* MobileCoreServices.framework in Frameworks */,
E2DDD2211BE6A06E002CE867 /* CFNetwork.framework in Frameworks */,
E2DDD2281BE6A0D8002CE867 /* libxml2.tbd in Frameworks */,
E2DDD2291BE6A0D8002CE867 /* libz.tbd in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -487,6 +470,7 @@
CEE28D081AE0053E00F4023C /* Frameworks */ = { CEE28D081AE0053E00F4023C /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E24A3C4021E2940600C58878 /* module.modulemap */,
CEE28CF31AE0051F00F4023C /* GCDWebServers.h */, CEE28CF31AE0051F00F4023C /* GCDWebServers.h */,
CEE28CF21AE0051F00F4023C /* Info.plist */, CEE28CF21AE0051F00F4023C /* Info.plist */,
E24039311BA092B7000B7089 /* Tests.m */, E24039311BA092B7000B7089 /* Tests.m */,
@@ -506,7 +490,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CEE28D691AE1ABAA00F4023C /* UIKit.framework */, CEE28D691AE1ABAA00F4023C /* UIKit.framework */,
E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */, E221129C1690B7BA0048D2B2 /* CoreServices.framework */,
E22112981690B7AA0048D2B2 /* CFNetwork.framework */, E22112981690B7AA0048D2B2 /* CFNetwork.framework */,
E2DDD2241BE6A0AE002CE867 /* libxml2.tbd */, E2DDD2241BE6A0AE002CE867 /* libxml2.tbd */,
E2DDD2261BE6A0B4002CE867 /* libz.tbd */, E2DDD2261BE6A0B4002CE867 /* libz.tbd */,
@@ -609,7 +593,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E2DDD1BF1BE69576002CE867 /* UIKit.framework */, E2DDD1BF1BE69576002CE867 /* UIKit.framework */,
E2DDD1BD1BE6956F002CE867 /* MobileCoreServices.framework */, E2DDD1BD1BE6956F002CE867 /* CoreServices.framework */,
E2DDD1B61BE6951A002CE867 /* CFNetwork.framework */, E2DDD1B61BE6951A002CE867 /* CFNetwork.framework */,
E2DDD1BB1BE69551002CE867 /* libxml2.tbd */, E2DDD1BB1BE69551002CE867 /* libxml2.tbd */,
E2DDD1B91BE69545002CE867 /* libz.tbd */, E2DDD1B91BE69545002CE867 /* libz.tbd */,
@@ -620,14 +604,11 @@
E2DDD1C81BE698A8002CE867 /* tvOS */ = { E2DDD1C81BE698A8002CE867 /* tvOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E2DDD1CC1BE698A8002CE867 /* AppDelegate.h */, E2DDD1CD1BE698A8002CE867 /* AppDelegate.swift */,
E2DDD1CD1BE698A8002CE867 /* AppDelegate.m */, E2DDD1D01BE698A8002CE867 /* ViewController.swift */,
E2DDD1CF1BE698A8002CE867 /* ViewController.h */,
E2DDD1D01BE698A8002CE867 /* ViewController.m */,
E2DDD1D21BE698A8002CE867 /* Main.storyboard */, E2DDD1D21BE698A8002CE867 /* Main.storyboard */,
E2DDD1D51BE698A8002CE867 /* Assets.xcassets */, E2DDD1D51BE698A8002CE867 /* Assets.xcassets */,
E2DDD1D71BE698A8002CE867 /* Info.plist */, E2DDD1D71BE698A8002CE867 /* Info.plist */,
E2DDD1CA1BE698A8002CE867 /* main.m */,
); );
path = tvOS; path = tvOS;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -635,15 +616,12 @@
E2DDD1F71BE69EE5002CE867 /* iOS */ = { E2DDD1F71BE69EE5002CE867 /* iOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E2DDD1FB1BE69EE5002CE867 /* AppDelegate.h */, E2DDD1FC1BE69EE5002CE867 /* AppDelegate.swift */,
E2DDD1FC1BE69EE5002CE867 /* AppDelegate.m */, E2DDD1FF1BE69EE5002CE867 /* ViewController.swift */,
E2DDD1FE1BE69EE5002CE867 /* ViewController.h */,
E2DDD1FF1BE69EE5002CE867 /* ViewController.m */,
E2DDD2011BE69EE5002CE867 /* Main.storyboard */, E2DDD2011BE69EE5002CE867 /* Main.storyboard */,
E2DDD2041BE69EE5002CE867 /* Assets.xcassets */, E2DDD2041BE69EE5002CE867 /* Assets.xcassets */,
E2DDD2061BE69EE5002CE867 /* LaunchScreen.storyboard */, E2DDD2061BE69EE5002CE867 /* LaunchScreen.storyboard */,
E2DDD2091BE69EE5002CE867 /* Info.plist */, E2DDD2091BE69EE5002CE867 /* Info.plist */,
E2DDD1F91BE69EE5002CE867 /* main.m */,
); );
path = iOS; path = iOS;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -821,12 +799,14 @@
buildConfigurationList = E2DDD1D81BE698A8002CE867 /* Build configuration list for PBXNativeTarget "GCDWebServer (tvOS)" */; buildConfigurationList = E2DDD1D81BE698A8002CE867 /* Build configuration list for PBXNativeTarget "GCDWebServer (tvOS)" */;
buildPhases = ( buildPhases = (
E2DDD1C51BE698A8002CE867 /* Resources */, E2DDD1C51BE698A8002CE867 /* Resources */,
E24A3C0D21E28D2300C58878 /* Copy Frameworks */,
E2DDD1C31BE698A8002CE867 /* Sources */, E2DDD1C31BE698A8002CE867 /* Sources */,
E2DDD1C41BE698A8002CE867 /* Frameworks */, E2DDD1C41BE698A8002CE867 /* Frameworks */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
E24A3C0621E2879700C58878 /* PBXTargetDependency */,
); );
name = "GCDWebServer (tvOS)"; name = "GCDWebServer (tvOS)";
productName = tvOS; productName = tvOS;
@@ -838,12 +818,14 @@
buildConfigurationList = E2DDD20A1BE69EE5002CE867 /* Build configuration list for PBXNativeTarget "GCDWebServer (iOS)" */; buildConfigurationList = E2DDD20A1BE69EE5002CE867 /* Build configuration list for PBXNativeTarget "GCDWebServer (iOS)" */;
buildPhases = ( buildPhases = (
E2DDD1F41BE69EE4002CE867 /* Resources */, E2DDD1F41BE69EE4002CE867 /* Resources */,
E24A3C0C21E28D1E00C58878 /* Copy Frameworks */,
E2DDD1F21BE69EE4002CE867 /* Sources */, E2DDD1F21BE69EE4002CE867 /* Sources */,
E2DDD1F31BE69EE4002CE867 /* Frameworks */, E2DDD1F31BE69EE4002CE867 /* Frameworks */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
E24A3C0421E2879000C58878 /* PBXTargetDependency */,
); );
name = "GCDWebServer (iOS)"; name = "GCDWebServer (iOS)";
productName = "GCDWebServer (iOS)"; productName = "GCDWebServer (iOS)";
@@ -856,7 +838,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = { 08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0900; LastUpgradeCheck = 1130;
TargetAttributes = { TargetAttributes = {
CEE28CD01AE004D800F4023C = { CEE28CD01AE004D800F4023C = {
CreatedOnToolsVersion = 6.3; CreatedOnToolsVersion = 6.3;
@@ -869,9 +851,11 @@
}; };
E2DDD18A1BE69404002CE867 = { E2DDD18A1BE69404002CE867 = {
CreatedOnToolsVersion = 7.1; CreatedOnToolsVersion = 7.1;
ProvisioningStyle = Manual;
}; };
E2DDD1C61BE698A8002CE867 = { E2DDD1C61BE698A8002CE867 = {
CreatedOnToolsVersion = 7.1; CreatedOnToolsVersion = 7.1;
ProvisioningStyle = Manual;
}; };
E2DDD1F51BE69EE4002CE867 = { E2DDD1F51BE69EE4002CE867 = {
CreatedOnToolsVersion = 7.1; CreatedOnToolsVersion = 7.1;
@@ -880,13 +864,9 @@
}; };
buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "GCDWebServer" */; buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "GCDWebServer" */;
compatibilityVersion = "Xcode 8.0"; compatibilityVersion = "Xcode 8.0";
developmentRegion = English; developmentRegion = en;
hasScannedForEncodings = 1; hasScannedForEncodings = 1;
knownRegions = ( knownRegions = (
English,
Japanese,
French,
German,
en, en,
Base, Base,
); );
@@ -935,7 +915,6 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E2DDD1F11BE69BE9002CE867 /* GCDWebUploader.bundle in Resources */,
E2DDD1D61BE698A8002CE867 /* Assets.xcassets in Resources */, E2DDD1D61BE698A8002CE867 /* Assets.xcassets in Resources */,
E2DDD1D41BE698A8002CE867 /* Main.storyboard in Resources */, E2DDD1D41BE698A8002CE867 /* Main.storyboard in Resources */,
); );
@@ -945,7 +924,6 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E2DDD20F1BE69F03002CE867 /* GCDWebUploader.bundle in Resources */,
E2DDD2081BE69EE5002CE867 /* LaunchScreen.storyboard in Resources */, E2DDD2081BE69EE5002CE867 /* LaunchScreen.storyboard in Resources */,
E2DDD2051BE69EE5002CE867 /* Assets.xcassets in Resources */, E2DDD2051BE69EE5002CE867 /* Assets.xcassets in Resources */,
E2DDD2031BE69EE5002CE867 /* Main.storyboard in Resources */, E2DDD2031BE69EE5002CE867 /* Main.storyboard in Resources */,
@@ -1073,24 +1051,8 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E2DDD1DE1BE69BB7002CE867 /* GCDWebServer.m in Sources */, E2DDD1D11BE698A8002CE867 /* ViewController.swift in Sources */,
E2DDD1DF1BE69BB7002CE867 /* GCDWebServerConnection.m in Sources */, E2DDD1CE1BE698A8002CE867 /* AppDelegate.swift in Sources */,
E2DDD1E01BE69BB7002CE867 /* GCDWebServerFunctions.m in Sources */,
E2DDD1E11BE69BB7002CE867 /* GCDWebServerRequest.m in Sources */,
E2DDD1E21BE69BB7002CE867 /* GCDWebServerResponse.m in Sources */,
E2DDD1E31BE69BB7002CE867 /* GCDWebServerDataRequest.m in Sources */,
E2DDD1E41BE69BB7002CE867 /* GCDWebServerFileRequest.m in Sources */,
E2DDD1E51BE69BB7002CE867 /* GCDWebServerMultiPartFormRequest.m in Sources */,
E2DDD1E61BE69BB7002CE867 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
E2DDD1E71BE69BB7002CE867 /* GCDWebServerDataResponse.m in Sources */,
E2DDD1E81BE69BB7002CE867 /* GCDWebServerErrorResponse.m in Sources */,
E2DDD1E91BE69BB7002CE867 /* GCDWebServerFileResponse.m in Sources */,
E2DDD1EA1BE69BB7002CE867 /* GCDWebServerStreamedResponse.m in Sources */,
E2DDD1EB1BE69BB7002CE867 /* GCDWebDAVServer.m in Sources */,
E2DDD1EC1BE69BB7002CE867 /* GCDWebUploader.m in Sources */,
E2DDD1D11BE698A8002CE867 /* ViewController.m in Sources */,
E2DDD1CE1BE698A8002CE867 /* AppDelegate.m in Sources */,
E2DDD1CB1BE698A8002CE867 /* main.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1098,24 +1060,8 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E2DDD2101BE69F17002CE867 /* GCDWebServer.m in Sources */, E2DDD2001BE69EE5002CE867 /* ViewController.swift in Sources */,
E2DDD2111BE69F17002CE867 /* GCDWebServerConnection.m in Sources */, E2DDD1FD1BE69EE5002CE867 /* AppDelegate.swift in Sources */,
E2DDD2121BE69F17002CE867 /* GCDWebServerFunctions.m in Sources */,
E2DDD2131BE69F17002CE867 /* GCDWebServerRequest.m in Sources */,
E2DDD2141BE69F17002CE867 /* GCDWebServerResponse.m in Sources */,
E2DDD2151BE69F17002CE867 /* GCDWebServerDataRequest.m in Sources */,
E2DDD2161BE69F17002CE867 /* GCDWebServerFileRequest.m in Sources */,
E2DDD2171BE69F17002CE867 /* GCDWebServerMultiPartFormRequest.m in Sources */,
E2DDD2181BE69F17002CE867 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
E2DDD2191BE69F17002CE867 /* GCDWebServerDataResponse.m in Sources */,
E2DDD21A1BE69F17002CE867 /* GCDWebServerErrorResponse.m in Sources */,
E2DDD21B1BE69F17002CE867 /* GCDWebServerFileResponse.m in Sources */,
E2DDD21C1BE69F17002CE867 /* GCDWebServerStreamedResponse.m in Sources */,
E2DDD21D1BE69F25002CE867 /* GCDWebDAVServer.m in Sources */,
E2DDD21E1BE69F25002CE867 /* GCDWebUploader.m in Sources */,
E2DDD2001BE69EE5002CE867 /* ViewController.m in Sources */,
E2DDD1FD1BE69EE5002CE867 /* AppDelegate.m in Sources */,
E2DDD1FA1BE69EE5002CE867 /* main.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1137,6 +1083,16 @@
target = CEE28CD01AE004D800F4023C /* GCDWebServers (Mac) */; target = CEE28CD01AE004D800F4023C /* GCDWebServers (Mac) */;
targetProxy = E240392C1BA09207000B7089 /* PBXContainerItemProxy */; targetProxy = E240392C1BA09207000B7089 /* PBXContainerItemProxy */;
}; };
E24A3C0421E2879000C58878 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = CEE28CEE1AE0051F00F4023C /* GCDWebServers (iOS) */;
targetProxy = E24A3C0321E2879000C58878 /* PBXContainerItemProxy */;
};
E24A3C0621E2879700C58878 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = E2DDD18A1BE69404002CE867 /* GCDWebServers (tvOS) */;
targetProxy = E24A3C0521E2879700C58878 /* PBXContainerItemProxy */;
};
E274F87B187E77E3009E0582 /* PBXTargetDependency */ = { E274F87B187E77E3009E0582 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = 8DD76FA90486AB0100D96B5E /* GCDWebServer (Mac) */; target = 8DD76FA90486AB0100D96B5E /* GCDWebServer (Mac) */;
@@ -1208,7 +1164,8 @@
1DEB928A08733DD80010E9CD /* Debug */ = { 1DEB928A08733DD80010E9CD /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
BUNDLE_VERSION_STRING = 3.3.4; ALWAYS_SEARCH_USER_PATHS = NO;
BUNDLE_VERSION_STRING = 3.5.4;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_OPTIMIZATION_LEVEL = 0; GCC_OPTIMIZATION_LEVEL = 0;
@@ -1217,16 +1174,20 @@
HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2";
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "net.pol-online.GCDWebServers"; PRODUCT_BUNDLE_IDENTIFIER = "net.pol-online.GCDWebServers";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
WARNING_CFLAGS = ( WARNING_CFLAGS = (
"-Wall", "-Wall",
"-Weverything", "-Weverything",
"-Wshadow", "-Wshadow",
"-Wshorten-64-to-32", "-Wshorten-64-to-32",
"-Wstrict-prototypes",
"-Wdeprecated-declarations",
"-Wno-vla", "-Wno-vla",
"-Wno-explicit-ownership-type", "-Wno-explicit-ownership-type",
"-Wno-gnu-statement-expression", "-Wno-gnu-statement-expression",
"-Wno-direct-ivar-access", "-Wno-direct-ivar-access",
"-Wno-implicit-retain-self",
"-Wno-assign-enum", "-Wno-assign-enum",
"-Wno-format-nonliteral", "-Wno-format-nonliteral",
"-Wno-cast-align", "-Wno-cast-align",
@@ -1237,7 +1198,6 @@
"-Wno-cstring-format-directive", "-Wno-cstring-format-directive",
"-Wno-reserved-id-macro", "-Wno-reserved-id-macro",
"-Wno-cast-qual", "-Wno-cast-qual",
"-Wno-partial-availability",
); );
}; };
name = Debug; name = Debug;
@@ -1245,13 +1205,17 @@
1DEB928B08733DD80010E9CD /* Release */ = { 1DEB928B08733DD80010E9CD /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
BUNDLE_VERSION_STRING = 3.3.4; ALWAYS_SEARCH_USER_PATHS = NO;
BUNDLE_VERSION_STRING = 3.5.4;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = __GCDWEBSERVER_ENABLE_TESTING__; GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = __GCDWEBSERVER_ENABLE_TESTING__;
GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2";
PRODUCT_BUNDLE_IDENTIFIER = "net.pol-online.GCDWebServers"; PRODUCT_BUNDLE_IDENTIFIER = "net.pol-online.GCDWebServers";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
SWIFT_VERSION = 5.0;
WARNING_CFLAGS = "-Wall"; WARNING_CFLAGS = "-Wall";
}; };
name = Release; name = Release;
@@ -1298,9 +1262,11 @@
INFOPLIST_FILE = Frameworks/Info.plist; INFOPLIST_FILE = Frameworks/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MODULEMAP_FILE = Frameworks/module.modulemap;
PRODUCT_NAME = GCDWebServers; PRODUCT_NAME = GCDWebServers;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
@@ -1314,9 +1280,11 @@
INFOPLIST_FILE = Frameworks/Info.plist; INFOPLIST_FILE = Frameworks/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MODULEMAP_FILE = Frameworks/module.modulemap;
PRODUCT_NAME = GCDWebServers; PRODUCT_NAME = GCDWebServers;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };
@@ -1373,13 +1341,16 @@
E2DDD1901BE69404002CE867 /* Debug */ = { E2DDD1901BE69404002CE867 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Frameworks/Info.plist; INFOPLIST_FILE = Frameworks/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = GCDWebServers; PRODUCT_NAME = GCDWebServers;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = appletvos; SDKROOT = appletvos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
@@ -1389,13 +1360,16 @@
E2DDD1911BE69404002CE867 /* Release */ = { E2DDD1911BE69404002CE867 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Frameworks/Info.plist; INFOPLIST_FILE = Frameworks/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = GCDWebServers; PRODUCT_NAME = GCDWebServers;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = appletvos; SDKROOT = appletvos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
@@ -1405,9 +1379,13 @@
E2DDD1D91BE698A8002CE867 /* Debug */ = { E2DDD1D91BE698A8002CE867 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = tvOS/Info.plist; INFOPLIST_FILE = tvOS/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = GCDWebServer; PRODUCT_NAME = GCDWebServer;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = appletvos; SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
}; };
@@ -1416,9 +1394,13 @@
E2DDD1DA1BE698A8002CE867 /* Release */ = { E2DDD1DA1BE698A8002CE867 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = tvOS/Info.plist; INFOPLIST_FILE = tvOS/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = GCDWebServer; PRODUCT_NAME = GCDWebServer;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = appletvos; SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
}; };
@@ -1427,11 +1409,12 @@
E2DDD20B1BE69EE5002CE867 /* Debug */ = { E2DDD20B1BE69EE5002CE867 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
COPY_PHASE_STRIP = NO;
ENABLE_BITCODE = YES; ENABLE_BITCODE = YES;
INFOPLIST_FILE = iOS/Info.plist; INFOPLIST_FILE = iOS/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = GCDWebServer; PRODUCT_NAME = GCDWebServer;
PROVISIONING_PROFILE = "";
SDKROOT = iphoneos; SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
@@ -1440,11 +1423,12 @@
E2DDD20C1BE69EE5002CE867 /* Release */ = { E2DDD20C1BE69EE5002CE867 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
COPY_PHASE_STRIP = NO;
ENABLE_BITCODE = YES; ENABLE_BITCODE = YES;
INFOPLIST_FILE = iOS/Info.plist; INFOPLIST_FILE = iOS/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = GCDWebServer; PRODUCT_NAME = GCDWebServer;
PROVISIONING_PROFILE = "";
SDKROOT = iphoneos; SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0900" LastUpgradeVersion = "1130"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -26,9 +26,8 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableAddressSanitizer = "YES" shouldUseLaunchSchemeArgsEnv = "YES"
language = "" enableAddressSanitizer = "YES">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
<TestableReference <TestableReference
skipped = "NO"> skipped = "NO">
@@ -41,14 +40,11 @@
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
</Testables> </Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -64,8 +60,6 @@
ReferencedContainer = "container:GCDWebServer.xcodeproj"> ReferencedContainer = "container:GCDWebServer.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0900" LastUpgradeVersion = "1130"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -26,10 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
@@ -39,14 +36,13 @@
ReferencedContainer = "container:GCDWebServer.xcodeproj"> ReferencedContainer = "container:GCDWebServer.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <Testables>
</AdditionalOptions> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -62,8 +58,6 @@
ReferencedContainer = "container:GCDWebServer.xcodeproj"> ReferencedContainer = "container:GCDWebServer.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0900" LastUpgradeVersion = "1130"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -26,10 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
@@ -39,14 +36,13 @@
ReferencedContainer = "container:GCDWebServer.xcodeproj"> ReferencedContainer = "container:GCDWebServer.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <Testables>
</AdditionalOptions> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -62,8 +58,6 @@
ReferencedContainer = "container:GCDWebServer.xcodeproj"> ReferencedContainer = "container:GCDWebServer.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
* GCDWebServerRequest instance created with the same basic info. * GCDWebServerRequest instance created with the same basic info.
* Otherwise, it simply returns nil. * Otherwise, it simply returns nil.
*/ */
typedef GCDWebServerRequest* _Nullable (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery); typedef GCDWebServerRequest* _Nullable (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery);
/** /**
* The GCDWebServerProcessBlock is called after the HTTP request has been fully * The GCDWebServerProcessBlock is called after the HTTP request has been fully
@@ -69,6 +69,13 @@ typedef GCDWebServerResponse* _Nullable (^GCDWebServerProcessBlock)(__kindof GCD
typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* _Nullable response); typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* _Nullable response);
typedef void (^GCDWebServerAsyncProcessBlock)(__kindof GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock); typedef void (^GCDWebServerAsyncProcessBlock)(__kindof GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock);
/**
* The GCDWebServerBuiltInLoggerBlock is used to override the built-in logger at runtime.
* The block will be passed the log level and the log message, see setLogLevel for
* documentation of the log levels for the built-in logger.
*/
typedef void (^GCDWebServerBuiltInLoggerBlock)(int level, NSString* _Nonnull message);
/** /**
* The port used by the GCDWebServer (NSNumber / NSUInteger). * The port used by the GCDWebServer (NSNumber / NSUInteger).
* *
@@ -85,6 +92,13 @@ extern NSString* const GCDWebServerOption_Port;
*/ */
extern NSString* const GCDWebServerOption_BonjourName; extern NSString* const GCDWebServerOption_BonjourName;
/**
* The Bonjour TXT Data used by the GCDWebServer (NSDictionary<NSString, NSString>).
*
* The default value is nil.
*/
extern NSString* const GCDWebServerOption_BonjourTXTData;
/** /**
* The Bonjour service type used by the GCDWebServer (NSString). * The Bonjour service type used by the GCDWebServer (NSString).
* *
@@ -365,7 +379,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
* *
* Returns NO if the server failed to start and sets "error" argument if not NULL. * Returns NO if the server failed to start and sets "error" argument if not NULL.
*/ */
- (BOOL)startWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error; - (BOOL)startWithOptions:(nullable NSDictionary<NSString*, id>*)options error:(NSError** _Nullable)error;
/** /**
* Stops the server and prevents it to accepts new HTTP requests. * Stops the server and prevents it to accepts new HTTP requests.
@@ -444,7 +458,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
* *
* @warning This method must be used from the main thread only. * @warning This method must be used from the main thread only.
*/ */
- (BOOL)runWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error; - (BOOL)runWithOptions:(nullable NSDictionary<NSString*, id>*)options error:(NSError** _Nullable)error;
#endif #endif
@@ -573,6 +587,14 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
*/ */
+ (void)setLogLevel:(int)level; + (void)setLogLevel:(int)level;
/**
* Set a logger to be used instead of the built-in logger which logs to stderr.
*
* IMPORTANT: In order for this override to work, you should not be specifying
* a custom logger at compile time with "__GCDWEBSERVER_LOGGING_HEADER__".
*/
+ (void)setBuiltInLogger:(GCDWebServerBuiltInLoggerBlock)block;
/** /**
* Logs a message to the logging facility at the VERBOSE level. * Logs a message to the logging facility at the VERBOSE level.
*/ */
@@ -613,7 +635,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
* *
* Returns the number of failed tests or -1 if server failed to start. * Returns the number of failed tests or -1 if server failed to start.
*/ */
- (NSInteger)runTestsWithOptions:(nullable NSDictionary*)options inDirectory:(NSString*)path; - (NSInteger)runTestsWithOptions:(nullable NSDictionary<NSString*, id>*)options inDirectory:(NSString*)path;
@end @end

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -53,6 +53,7 @@
NSString* const GCDWebServerOption_Port = @"Port"; NSString* const GCDWebServerOption_Port = @"Port";
NSString* const GCDWebServerOption_BonjourName = @"BonjourName"; NSString* const GCDWebServerOption_BonjourName = @"BonjourName";
NSString* const GCDWebServerOption_BonjourType = @"BonjourType"; NSString* const GCDWebServerOption_BonjourType = @"BonjourType";
NSString* const GCDWebServerOption_BonjourTXTData = @"BonjourTXTData";
NSString* const GCDWebServerOption_RequestNATPortMapping = @"RequestNATPortMapping"; NSString* const GCDWebServerOption_RequestNATPortMapping = @"RequestNATPortMapping";
NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost"; NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost";
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections"; NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
@@ -85,18 +86,24 @@ static BOOL _run;
#ifdef __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__ #ifdef __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__
static GCDWebServerBuiltInLoggerBlock _builtInLoggerBlock;
void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) { void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) {
static const char* levelNames[] = {"DEBUG", "VERBOSE", "INFO", "WARNING", "ERROR"}; static const char* levelNames[] = {"DEBUG", "VERBOSE", "INFO", "WARNING", "ERROR"};
static int enableLogging = -1; static int enableLogging = -1;
if (enableLogging < 0) { if (enableLogging < 0) {
enableLogging = (isatty(STDERR_FILENO) ? 1 : 0); enableLogging = (isatty(STDERR_FILENO) ? 1 : 0);
} }
if (enableLogging) { if (_builtInLoggerBlock || enableLogging) {
va_list arguments; va_list arguments;
va_start(arguments, format); va_start(arguments, format);
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
va_end(arguments); va_end(arguments);
fprintf(stderr, "[%s] %s\n", levelNames[level], [message UTF8String]); if (_builtInLoggerBlock) {
_builtInLoggerBlock(level, message);
} else {
fprintf(stderr, "[%s] %s\n", levelNames[level], [message UTF8String]);
}
} }
} }
@@ -141,14 +148,14 @@ static void _ExecuteMainThreadRunLoopSources() {
@implementation GCDWebServer { @implementation GCDWebServer {
dispatch_queue_t _syncQueue; dispatch_queue_t _syncQueue;
dispatch_group_t _sourceGroup; dispatch_group_t _sourceGroup;
NSMutableArray* _handlers; NSMutableArray<GCDWebServerHandler*>* _handlers;
NSInteger _activeConnections; // Accessed through _syncQueue only NSInteger _activeConnections; // Accessed through _syncQueue only
BOOL _connected; // Accessed on main thread only BOOL _connected; // Accessed on main thread only
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
NSDictionary* _options; NSDictionary<NSString*, id>* _options;
NSMutableDictionary* _authenticationBasicAccounts; NSMutableDictionary<NSString*, NSString*>* _authenticationBasicAccounts;
NSMutableDictionary* _authenticationDigestAccounts; NSMutableDictionary<NSString*, NSString*>* _authenticationDigestAccounts;
Class _connectionClass; Class _connectionClass;
CFTimeInterval _disconnectDelay; CFTimeInterval _disconnectDelay;
dispatch_source_t _source4; dispatch_source_t _source4;
@@ -206,10 +213,8 @@ static void _ExecuteMainThreadRunLoopSources() {
if (_backgroundTask == UIBackgroundTaskInvalid) { if (_backgroundTask == UIBackgroundTaskInvalid) {
GWS_LOG_DEBUG(@"Did start background task"); GWS_LOG_DEBUG(@"Did start background task");
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]); GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]);
[self _endBackgroundTask]; [self _endBackgroundTask];
}]; }];
} else { } else {
GWS_DNOT_REACHED(); GWS_DNOT_REACHED();
@@ -238,22 +243,20 @@ static void _ExecuteMainThreadRunLoopSources() {
- (void)willStartConnection:(GCDWebServerConnection*)connection { - (void)willStartConnection:(GCDWebServerConnection*)connection {
dispatch_sync(_syncQueue, ^{ dispatch_sync(_syncQueue, ^{
GWS_DCHECK(self->_activeConnections >= 0);
GWS_DCHECK(_activeConnections >= 0); if (self->_activeConnections == 0) {
if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnectTimer) { if (self->_disconnectTimer) {
CFRunLoopTimerInvalidate(_disconnectTimer); CFRunLoopTimerInvalidate(self->_disconnectTimer);
CFRelease(_disconnectTimer); CFRelease(self->_disconnectTimer);
_disconnectTimer = NULL; self->_disconnectTimer = NULL;
} }
if (_connected == NO) { if (self->_connected == NO) {
[self _didConnect]; [self _didConnect];
} }
}); });
} }
_activeConnections += 1; self->_activeConnections += 1;
}); });
} }
@@ -292,22 +295,22 @@ static void _ExecuteMainThreadRunLoopSources() {
- (void)didEndConnection:(GCDWebServerConnection*)connection { - (void)didEndConnection:(GCDWebServerConnection*)connection {
dispatch_sync(_syncQueue, ^{ dispatch_sync(_syncQueue, ^{
GWS_DCHECK(_activeConnections > 0); GWS_DCHECK(self->_activeConnections > 0);
_activeConnections -= 1; self->_activeConnections -= 1;
if (_activeConnections == 0) { if (self->_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if ((_disconnectDelay > 0.0) && (_source4 != NULL)) { if ((self->_disconnectDelay > 0.0) && (self->_source4 != NULL)) {
if (_disconnectTimer) { if (self->_disconnectTimer) {
CFRunLoopTimerInvalidate(_disconnectTimer); CFRunLoopTimerInvalidate(self->_disconnectTimer);
CFRelease(_disconnectTimer); CFRelease(self->_disconnectTimer);
} }
_disconnectTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + _disconnectDelay, 0.0, 0, 0, ^(CFRunLoopTimerRef timer) { self->_disconnectTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + self->_disconnectDelay, 0.0, 0, 0, ^(CFRunLoopTimerRef timer) {
GWS_DCHECK([NSThread isMainThread]); GWS_DCHECK([NSThread isMainThread]);
[self _didDisconnect]; [self _didDisconnect];
CFRelease(_disconnectTimer); CFRelease(self->_disconnectTimer);
_disconnectTimer = NULL; self->_disconnectTimer = NULL;
}); });
CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes); CFRunLoopAddTimer(CFRunLoopGetMain(), self->_disconnectTimer, kCFRunLoopCommonModes);
} else { } else {
[self _didDisconnect]; [self _didDisconnect];
} }
@@ -412,19 +415,21 @@ static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef
} }
} }
static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValue) { static inline id _GetOption(NSDictionary<NSString*, id>* options, NSString* key, id defaultValue) {
id value = [options objectForKey:key]; id value = [options objectForKey:key];
return value ? value : defaultValue; return value ? value : defaultValue;
} }
static inline NSString* _EncodeBase64(NSString* string) { static inline NSString* _EncodeBase64(NSString* string) {
NSData* data = [string dataUsingEncoding:NSUTF8StringEncoding]; 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 TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9)
if (![data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) {
return [data base64Encoding];
}
#endif
return [[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding]; return [[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding];
#else
if (@available(macOS 10.9, *)) {
return [[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding];
}
return [data base64Encoding];
#endif
} }
- (int)_createListeningSocket:(BOOL)useIPv6 - (int)_createListeningSocket:(BOOL)useIPv6
@@ -469,7 +474,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
dispatch_group_enter(_sourceGroup); dispatch_group_enter(_sourceGroup);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0)); dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0));
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) {
@@ -478,11 +482,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
GWS_LOG_DEBUG(@"Did close %s listening socket %i", isIPv6 ? "IPv6" : "IPv4", listeningSocket); GWS_LOG_DEBUG(@"Did close %s listening socket %i", isIPv6 ? "IPv6" : "IPv4", listeningSocket);
} }
} }
dispatch_group_leave(_sourceGroup); dispatch_group_leave(self->_sourceGroup);
}); });
dispatch_source_set_event_handler(source, ^{ dispatch_source_set_event_handler(source, ^{
@autoreleasepool { @autoreleasepool {
struct sockaddr_storage remoteSockAddr; struct sockaddr_storage remoteSockAddr;
socklen_t remoteAddrLen = sizeof(remoteSockAddr); socklen_t remoteAddrLen = sizeof(remoteSockAddr);
@@ -503,13 +505,12 @@ static inline NSString* _EncodeBase64(NSString* string) {
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
GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened GCDWebServerConnection* connection = [(GCDWebServerConnection*)[self->_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
[connection self]; // Prevent compiler from complaining about unused variable / useless statement [connection self]; // Prevent compiler from complaining about unused variable / useless statement
} else { } else {
GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
} }
} }
}); });
return source; return source;
} }
@@ -517,9 +518,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (BOOL)_start:(NSError**)error { - (BOOL)_start:(NSError**)error {
GWS_DCHECK(_source4 == NULL); GWS_DCHECK(_source4 == NULL);
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue]; NSUInteger port = [(NSNumber*)_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue]; BOOL bindToLocalhost = [(NSNumber*)_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue]; NSUInteger maxPendingConnections = [(NSNumber*)_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
struct sockaddr_in addr4; struct sockaddr_in addr4;
bzero(&addr4, sizeof(addr4)); bzero(&addr4, sizeof(addr4));
@@ -553,27 +554,27 @@ static inline NSString* _EncodeBase64(NSString* string) {
return NO; return NO;
} }
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy]; _serverName = [(NSString*)_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil); NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) { if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy]; _authenticationRealm = [(NSString*)_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationBasicAccounts = [[NSMutableDictionary alloc] init]; _authenticationBasicAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{}); NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) { [accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username]; [self->_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username];
}]; }];
} else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) { } else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy]; _authenticationRealm = [(NSString*)_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationDigestAccounts = [[NSMutableDictionary alloc] init]; _authenticationDigestAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{}); NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) { [accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, _authenticationRealm, password) forKey:username]; [self->_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, self->_authenticationRealm, password) forKey:username];
}]; }];
} }
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]); _connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
_shouldAutomaticallyMapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue]; _shouldAutomaticallyMapHEADToGET = [(NSNumber*)_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue]; _disconnectDelay = [(NSNumber*)_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
_dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue]; _dispatchQueuePriority = [(NSNumber*)_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue];
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO]; _source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES]; _source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
@@ -590,6 +591,29 @@ static inline NSString* _EncodeBase64(NSString* string) {
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context); CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes); CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFStreamError streamError = {0}; CFStreamError streamError = {0};
NSDictionary* txtDataDictionary = _GetOption(_options, GCDWebServerOption_BonjourTXTData, nil);
if (txtDataDictionary != nil) {
NSUInteger count = txtDataDictionary.count;
CFStringRef keys[count];
CFStringRef values[count];
NSUInteger index = 0;
for (NSString *key in txtDataDictionary) {
NSString *value = txtDataDictionary[key];
keys[index] = (__bridge CFStringRef)(key);
values[index] = (__bridge CFStringRef)(value);
index ++;
}
CFDictionaryRef txtDictionary = CFDictionaryCreate(CFAllocatorGetDefault(), (void *)keys, (void *)values, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (txtDictionary != NULL) {
CFDataRef txtData = CFNetServiceCreateTXTDataWithDictionary(nil, txtDictionary);
Boolean setTXTDataResult = CFNetServiceSetTXTData(_registrationService, txtData);
if (!setTXTDataResult) {
GWS_LOG_ERROR(@"Failed setting TXTData");
}
}
}
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError); CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService); _resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
@@ -604,7 +628,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
} }
} }
if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) { if ([(NSNumber*)_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self); DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self);
if (status == kDNSServiceErr_NoError) { if (status == kDNSServiceErr_NoError) {
CFSocketContext context = {0, (__bridge void*)self, NULL, NULL, NULL}; CFSocketContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
@@ -632,7 +656,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL); GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
if ([_delegate respondsToSelector:@selector(webServerDidStart:)]) { if ([_delegate respondsToSelector:@selector(webServerDidStart:)]) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webServerDidStart:self]; [self->_delegate webServerDidStart:self];
}); });
} }
@@ -693,10 +717,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
_authenticationDigestAccounts = nil; _authenticationDigestAccounts = nil;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnectTimer) { if (self->_disconnectTimer) {
CFRunLoopTimerInvalidate(_disconnectTimer); CFRunLoopTimerInvalidate(self->_disconnectTimer);
CFRelease(_disconnectTimer); CFRelease(self->_disconnectTimer);
_disconnectTimer = NULL; self->_disconnectTimer = NULL;
[self _didDisconnect]; [self _didDisconnect];
} }
}); });
@@ -704,7 +728,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
GWS_LOG_INFO(@"%@ stopped", [self class]); GWS_LOG_INFO(@"%@ stopped", [self class]);
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) { if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webServerDidStop:self]; [self->_delegate webServerDidStop:self];
}); });
} }
} }
@@ -729,11 +753,11 @@ static inline NSString* _EncodeBase64(NSString* string) {
#endif #endif
- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error { - (BOOL)startWithOptions:(NSDictionary<NSString*, id>*)options error:(NSError**)error {
if (_options == nil) { if (_options == nil) {
_options = options ? [options copy] : @{}; _options = options ? [options copy] : @{};
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
_suspendInBackground = [_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue]; _suspendInBackground = [(NSNumber*)_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue];
if (((_suspendInBackground == NO) || ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground)) && ![self _start:error]) if (((_suspendInBackground == NO) || ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground)) && ![self _start:error])
#else #else
if (![self _start:error]) if (![self _start:error])
@@ -840,7 +864,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
return [self runWithOptions:options error:NULL]; return [self runWithOptions:options error:NULL];
} }
- (BOOL)runWithOptions:(NSDictionary*)options error:(NSError**)error { - (BOOL)runWithOptions:(NSDictionary<NSString*, id>*)options error:(NSError**)error {
GWS_DCHECK([NSThread isMainThread]); GWS_DCHECK([NSThread isMainThread]);
BOOL success = NO; BOOL success = NO;
_run = YES; _run = YES;
@@ -876,15 +900,14 @@ static inline NSString* _EncodeBase64(NSString* string) {
} }
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { - (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { [self
addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery) {
if (![requestMethod isEqualToString:method]) { if (![requestMethod isEqualToString:method]) {
return nil; return nil;
} }
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; return [(GCDWebServerRequest*)[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
}
} asyncProcessBlock:block];
asyncProcessBlock:block];
} }
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block { - (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
@@ -898,18 +921,17 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { - (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) { if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { [self
addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery) {
if (![requestMethod isEqualToString:method]) { if (![requestMethod isEqualToString:method]) {
return nil; return nil;
} }
if ([urlPath caseInsensitiveCompare:path] != NSOrderedSame) { if ([urlPath caseInsensitiveCompare:path] != NSOrderedSame) {
return nil; return nil;
} }
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; return [(GCDWebServerRequest*)[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
}
} asyncProcessBlock:block];
asyncProcessBlock:block];
} else { } else {
GWS_DNOT_REACHED(); GWS_DNOT_REACHED();
} }
@@ -927,36 +949,35 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { - (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL]; NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL];
if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) { if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { [self
addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery) {
if (![requestMethod isEqualToString:method]) { if (![requestMethod isEqualToString:method]) {
return nil; return nil;
}
NSArray* matches = [expression matchesInString:urlPath options:0 range:NSMakeRange(0, urlPath.length)];
if (matches.count == 0) {
return nil;
}
NSMutableArray* captures = [NSMutableArray array];
for (NSTextCheckingResult* result in matches) {
// Start at 1; index 0 is the whole string
for (NSUInteger i = 1; i < result.numberOfRanges; i++) {
NSRange range = [result rangeAtIndex:i];
// range is {NSNotFound, 0} "if one of the capture groups did not participate in this particular match"
// see discussion in -[NSRegularExpression firstMatchInString:options:range:]
if (range.location != NSNotFound) {
[captures addObject:[urlPath substringWithRange:range]];
} }
NSArray* matches = [expression matchesInString:urlPath options:0 range:NSMakeRange(0, urlPath.length)];
if (matches.count == 0) {
return nil;
}
NSMutableArray* captures = [NSMutableArray array];
for (NSTextCheckingResult* result in matches) {
// Start at 1; index 0 is the whole string
for (NSUInteger i = 1; i < result.numberOfRanges; i++) {
NSRange range = [result rangeAtIndex:i];
// range is {NSNotFound, 0} "if one of the capture groups did not participate in this particular match"
// see discussion in -[NSRegularExpression firstMatchInString:options:range:]
if (range.location != NSNotFound) {
[captures addObject:[urlPath substringWithRange:range]];
}
}
}
GCDWebServerRequest* request = [(GCDWebServerRequest*)[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
return request;
} }
} asyncProcessBlock:block];
GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
return request;
}
asyncProcessBlock:block];
} else { } else {
GWS_DNOT_REACHED(); GWS_DNOT_REACHED();
} }
@@ -971,11 +992,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
path:path path:path
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType]; GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
response.cacheControlMaxAge = cacheAge; response.cacheControlMaxAge = cacheAge;
return response; return response;
}]; }];
} }
@@ -984,7 +1003,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
path:path path:path
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
GCDWebServerResponse* response = nil; GCDWebServerResponse* response = nil;
if (allowRangeRequests) { if (allowRangeRequests) {
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment]; response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
@@ -994,34 +1012,33 @@ static inline NSString* _EncodeBase64(NSString* string) {
} }
response.cacheControlMaxAge = cacheAge; response.cacheControlMaxAge = cacheAge;
return response; return response;
}]; }];
} }
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path { - (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:path]; NSArray* contents = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
if (enumerator == nil) { if (contents == nil) {
return nil; return nil;
} }
NSMutableString* html = [NSMutableString string]; NSMutableString* html = [NSMutableString string];
[html appendString:@"<!DOCTYPE html>\n"]; [html appendString:@"<!DOCTYPE html>\n"];
[html appendString:@"<html><head><meta charset=\"utf-8\"></head><body>\n"]; [html appendString:@"<html><head><meta charset=\"utf-8\"></head><body>\n"];
[html appendString:@"<ul>\n"]; [html appendString:@"<ul>\n"];
for (NSString* file in enumerator) { for (NSString* entry in contents) {
if (![file hasPrefix:@"."]) { if (![entry hasPrefix:@"."]) {
NSString* type = [[enumerator fileAttributes] objectForKey:NSFileType]; NSString* type = [[[NSFileManager defaultManager] attributesOfItemAtPath:[path stringByAppendingPathComponent:entry] error:NULL] objectForKey:NSFileType];
GWS_DCHECK(type);
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSString* escapedFile = [file stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSString* escapedFile = [entry stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
#pragma clang diagnostic pop #pragma clang diagnostic pop
GWS_DCHECK(escapedFile); GWS_DCHECK(escapedFile);
if ([type isEqualToString:NSFileTypeRegular]) { if ([type isEqualToString:NSFileTypeRegular]) {
[html appendFormat:@"<li><a href=\"%@\">%@</a></li>\n", escapedFile, file]; [html appendFormat:@"<li><a href=\"%@\">%@</a></li>\n", escapedFile, entry];
} else if ([type isEqualToString:NSFileTypeDirectory]) { } else if ([type isEqualToString:NSFileTypeDirectory]) {
[html appendFormat:@"<li><a href=\"%@/\">%@/</a></li>\n", escapedFile, file]; [html appendFormat:@"<li><a href=\"%@/\">%@/</a></li>\n", escapedFile, entry];
} }
} }
[enumerator skipDescendents];
} }
[html appendString:@"</ul>\n"]; [html appendString:@"</ul>\n"];
[html appendString:@"</body></html>\n"]; [html appendString:@"</body></html>\n"];
@@ -1031,21 +1048,19 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests { - (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) { if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
GCDWebServer* __unsafe_unretained server = self; GCDWebServer* __unsafe_unretained server = self;
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { [self
addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery) {
if (![requestMethod isEqualToString:@"GET"]) { if (![requestMethod isEqualToString:@"GET"]) {
return nil; return nil;
} }
if (![urlPath hasPrefix:basePath]) { if (![urlPath hasPrefix:basePath]) {
return nil; return nil;
} }
return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
}
}
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
GCDWebServerResponse* response = nil; GCDWebServerResponse* response = nil;
NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]]; NSString* filePath = [directoryPath stringByAppendingPathComponent:GCDWebServerNormalizePath([request.path substringFromIndex:basePath.length])];
NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType]; NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
if (fileType) { if (fileType) {
if ([fileType isEqualToString:NSFileTypeDirectory]) { if ([fileType isEqualToString:NSFileTypeDirectory]) {
@@ -1072,7 +1087,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound]; response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
} }
return response; return response;
}]; }];
} else { } else {
GWS_DNOT_REACHED(); GWS_DNOT_REACHED();
@@ -1091,6 +1105,14 @@ static inline NSString* _EncodeBase64(NSString* string) {
#endif #endif
} }
+ (void)setBuiltInLogger:(GCDWebServerBuiltInLoggerBlock)block {
#if defined(__GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__)
_builtInLoggerBlock = block;
#else
GWS_DNOT_REACHED(); // Built-in logger must be enabled in order to override
#endif
}
- (void)logVerbose:(NSString*)format, ... { - (void)logVerbose:(NSString*)format, ... {
va_list arguments; va_list arguments;
va_start(arguments, format); va_start(arguments, format);
@@ -1148,9 +1170,9 @@ static CFHTTPMessageRef _CreateHTTPMessageFromPerformingRequest(NSData* inData,
if (httpSocket > 0) { if (httpSocket > 0) {
struct sockaddr_in addr4; struct sockaddr_in addr4;
bzero(&addr4, sizeof(addr4)); bzero(&addr4, sizeof(addr4));
addr4.sin_len = sizeof(port); addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET; addr4.sin_family = AF_INET;
addr4.sin_port = htons(8080); addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = htonl(INADDR_ANY); addr4.sin_addr.s_addr = htonl(INADDR_ANY);
if (connect(httpSocket, (void*)&addr4, sizeof(addr4)) == 0) { if (connect(httpSocket, (void*)&addr4, sizeof(addr4)) == 0) {
if (write(httpSocket, inData.bytes, inData.length) == (ssize_t)inData.length) { if (write(httpSocket, inData.bytes, inData.length) == (ssize_t)inData.length) {
@@ -1190,7 +1212,7 @@ static void _LogResult(NSString* format, ...) {
fprintf(stdout, "%s\n", [message UTF8String]); fprintf(stdout, "%s\n", [message UTF8String]);
} }
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path { - (NSInteger)runTestsWithOptions:(NSDictionary<NSString*, id>*)options inDirectory:(NSString*)path {
GWS_DCHECK([NSThread isMainThread]); GWS_DCHECK([NSThread isMainThread]);
NSArray* ignoredHeaders = @[ @"Date", @"Etag" ]; // Dates are always different by definition and ETags depend on file system node IDs NSArray* ignoredHeaders = @[ @"Date", @"Etag" ]; // Dates are always different by definition and ETags depend on file system node IDs
NSInteger result = -1; NSInteger result = -1;
@@ -1198,7 +1220,7 @@ static void _LogResult(NSString* format, ...) {
_ExecuteMainThreadRunLoopSources(); _ExecuteMainThreadRunLoopSources();
result = 0; result = 0;
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL]; NSArray* files = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
for (NSString* requestFile in files) { for (NSString* requestFile in files) {
if (![requestFile hasSuffix:@".request"]) { if (![requestFile hasSuffix:@".request"]) {
continue; continue;
@@ -1258,7 +1280,7 @@ static void _LogResult(NSString* format, ...) {
if ([actualContentLength isEqualToString:expectedContentLength] && (actualBody.length > expectedBody.length)) { // Handle web browser closing connection before retrieving entire body (e.g. when playing a video file) if ([actualContentLength isEqualToString:expectedContentLength] && (actualBody.length > expectedBody.length)) { // Handle web browser closing connection before retrieving entire body (e.g. when playing a video file)
actualBody = [actualBody subdataWithRange:NSMakeRange(0, expectedBody.length)]; actualBody = [actualBody subdataWithRange:NSMakeRange(0, expectedBody.length)];
} }
if (![actualBody isEqualToData:expectedBody]) { if ((actualBody && expectedBody && ![actualBody isEqualToData:expectedBody]) || (actualBody && !expectedBody) || (!actualBody && expectedBody)) {
_LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length); _LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
success = NO; success = NO;
#if !TARGET_OS_IPHONE #if !TARGET_OS_IPHONE

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -130,7 +130,7 @@ NS_ASSUME_NONNULL_BEGIN
* *
* The default implementation returns the original URL. * The default implementation returns the original URL.
*/ */
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers; - (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary<NSString*, NSString*>*)headers;
/** /**
* Assuming a valid HTTP request was received, this method is called before * Assuming a valid HTTP request was received, this method is called before

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -193,22 +193,18 @@ NS_ASSUME_NONNULL_END
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked")); CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked"));
} }
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { [_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); CFHTTPMessageSetHeaderFieldValue(self->_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
}]; }];
[self writeHeadersWithCompletionBlock:^(BOOL success) { [self writeHeadersWithCompletionBlock:^(BOOL success) {
if (success) { if (success) {
if (hasBody) { if (hasBody) {
[self writeBodyWithCompletionBlock:^(BOOL successInner) { [self writeBodyWithCompletionBlock:^(BOOL successInner) {
[self->_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
[_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
}]; }];
} }
} else if (hasBody) { } else if (hasBody) {
[_response performClose]; [self->_response performClose];
} }
}]; }];
} else { } else {
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
@@ -238,15 +234,13 @@ NS_ASSUME_NONNULL_END
if (length) { if (length) {
[self readBodyWithRemainingLength:length [self readBodyWithRemainingLength:length
completionBlock:^(BOOL success) { completionBlock:^(BOOL success) {
NSError* localError = nil; NSError* localError = nil;
if ([_request performClose:&localError]) { if ([self->_request performClose:&localError]) {
[self _startProcessingRequest]; [self _startProcessingRequest];
} else { } else {
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
} }
}]; }];
} else { } else {
if ([_request performClose:&error]) { if ([_request performClose:&error]) {
@@ -269,15 +263,13 @@ NS_ASSUME_NONNULL_END
NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData]; NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
[self readNextBodyChunk:chunkData [self readNextBodyChunk:chunkData
completionBlock:^(BOOL success) { completionBlock:^(BOOL success) {
NSError* localError = nil; NSError* localError = nil;
if ([_request performClose:&localError]) { if ([self->_request performClose:&localError]) {
[self _startProcessingRequest]; [self _startProcessingRequest];
} else { } else {
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
} }
}]; }];
} }
@@ -286,15 +278,14 @@ NS_ASSUME_NONNULL_END
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity]; NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
[self readHeaders:headersData [self readHeaders:headersData
withCompletionBlock:^(NSData* extraData) { withCompletionBlock:^(NSData* extraData) {
if (extraData) { if (extraData) {
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(self->_requestMessage)); // Method verbs are case-sensitive and uppercase
if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) { if (self->_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
requestMethod = @"GET"; requestMethod = @"GET";
_virtualHEAD = YES; self->_virtualHEAD = YES;
} }
NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(self->_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage)); NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(self->_requestMessage));
if (requestURL) { if (requestURL) {
requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders]; requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
GWS_DCHECK(requestURL); GWS_DCHECK(requestURL);
@@ -307,55 +298,53 @@ NS_ASSUME_NONNULL_END
NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped; NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{}; NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) { if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
for (_handler in _server.handlers) { for (self->_handler in self->_server.handlers) {
_request = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery); self->_request = self->_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
if (_request) { if (self->_request) {
break; break;
} }
} }
if (_request) { if (self->_request) {
_request.localAddressData = self.localAddressData; self->_request.localAddressData = self.localAddressData;
_request.remoteAddressData = self.remoteAddressData; self->_request.remoteAddressData = self.remoteAddressData;
if ([_request hasBody]) { if ([self->_request hasBody]) {
[_request prepareForWriting]; [self->_request prepareForWriting];
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) { if (self->_request.usesChunkedTransferEncoding || (extraData.length <= self->_request.contentLength)) {
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"]; NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
if (expectHeader) { if (expectHeader) {
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
[self writeData:_continueData [self writeData:_continueData
withCompletionBlock:^(BOOL success) { withCompletionBlock:^(BOOL success) {
if (success) { if (success) {
if (_request.usesChunkedTransferEncoding) { if (self->_request.usesChunkedTransferEncoding) {
[self _readChunkedBodyWithInitialData:extraData]; [self _readChunkedBodyWithInitialData:extraData];
} else { } else {
[self _readBodyWithLength:_request.contentLength initialData:extraData]; [self _readBodyWithLength:self->_request.contentLength initialData:extraData];
} }
} }
}]; }];
} else { } else {
GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket); GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", self->_socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed]; [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
} }
} else { } else {
if (_request.usesChunkedTransferEncoding) { if (self->_request.usesChunkedTransferEncoding) {
[self _readChunkedBodyWithInitialData:extraData]; [self _readChunkedBodyWithInitialData:extraData];
} else { } else {
[self _readBodyWithLength:_request.contentLength initialData:extraData]; [self _readBodyWithLength:self->_request.contentLength initialData:extraData];
} }
} }
} else { } else {
GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket); GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", self->_socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest]; [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
} }
} else { } else {
[self _startProcessingRequest]; [self _startProcessingRequest];
} }
} else { } else {
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery]; self->_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
GWS_DCHECK(_request); GWS_DCHECK(self->_request);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented]; [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented];
} }
} else { } else {
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
@@ -364,7 +353,6 @@ NS_ASSUME_NONNULL_END
} else { } else {
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
} }
}]; }];
} }
@@ -426,7 +414,6 @@ NS_ASSUME_NONNULL_END
- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block { - (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) { dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(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);
@@ -439,19 +426,18 @@ NS_ASSUME_NONNULL_END
[self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)]; [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
block(YES); block(YES);
} else { } else {
if (_totalBytesRead > 0) { if (self->_totalBytesRead > 0) {
GWS_LOG_ERROR(@"No more data available on socket %i", _socket); GWS_LOG_ERROR(@"No more data available on socket %i", self->_socket);
} else { } else {
GWS_LOG_WARNING(@"No data received from socket %i", _socket); GWS_LOG_WARNING(@"No data received from socket %i", self->_socket);
} }
block(NO); block(NO);
} }
} else { } else {
GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error); GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", self->_socket, strerror(error), error);
block(NO); block(NO);
} }
} }
}); });
} }
@@ -460,29 +446,27 @@ NS_ASSUME_NONNULL_END
[self readData:headersData [self readData:headersData
withLength:NSUIntegerMax withLength:NSUIntegerMax
completionBlock:^(BOOL success) { completionBlock:^(BOOL success) {
if (success) { if (success) {
NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)]; NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
if (range.location == NSNotFound) { if (range.location == NSNotFound) {
[self readHeaders:headersData withCompletionBlock:block]; [self readHeaders:headersData withCompletionBlock:block];
} else { } else {
NSUInteger length = range.location + range.length; NSUInteger length = range.location + range.length;
if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) { if (CFHTTPMessageAppendBytes(self->_requestMessage, headersData.bytes, length)) {
if (CFHTTPMessageIsHeaderComplete(_requestMessage)) { if (CFHTTPMessageIsHeaderComplete(self->_requestMessage)) {
block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]); block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
} else { } else {
GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket); GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", self->_socket);
block(nil); block(nil);
} }
} else { } else {
GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket); GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", self->_socket);
block(nil); block(nil);
} }
} }
} else { } else {
block(nil); block(nil);
} }
}]; }];
} }
@@ -492,11 +476,10 @@ NS_ASSUME_NONNULL_END
[self readData:bodyData [self readData:bodyData
withLength:length withLength:length
completionBlock:^(BOOL success) { completionBlock:^(BOOL success) {
if (success) { if (success) {
if (bodyData.length <= length) { if (bodyData.length <= length) {
NSError* error = nil; NSError* error = nil;
if ([_request performWriteData:bodyData error:&error]) { if ([self->_request performWriteData:bodyData error:&error]) {
NSUInteger remainingLength = length - bodyData.length; NSUInteger remainingLength = length - bodyData.length;
if (remainingLength) { if (remainingLength) {
[self readBodyWithRemainingLength:remainingLength completionBlock:block]; [self readBodyWithRemainingLength:remainingLength completionBlock:block];
@@ -504,18 +487,17 @@ NS_ASSUME_NONNULL_END
block(YES); block(YES);
} }
} else { } else {
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", self->_socket, error);
block(NO); block(NO);
} }
} else { } else {
GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket); GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", self->_socket);
block(NO); block(NO);
GWS_DNOT_REACHED(); GWS_DNOT_REACHED();
} }
} else { } else {
block(NO); block(NO);
} }
}]; }];
} }
@@ -575,13 +557,11 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
[self readData:chunkData [self readData:chunkData
withLength:NSUIntegerMax withLength:NSUIntegerMax
completionBlock:^(BOOL success) { completionBlock:^(BOOL success) {
if (success) { if (success) {
[self readNextBodyChunk:chunkData completionBlock:block]; [self readNextBodyChunk:chunkData completionBlock:block];
} else { } else {
block(NO); block(NO);
} }
}]; }];
} }
@@ -594,18 +574,16 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
[data self]; // Keeps ARC from releasing data too early [data self]; // Keeps ARC from releasing data too early
}); });
dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) { dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
@autoreleasepool { @autoreleasepool {
if (error == 0) { if (error == 0) {
GWS_DCHECK(remainingData == NULL); GWS_DCHECK(remainingData == NULL);
[self didWriteBytes:data.bytes length:data.length]; [self didWriteBytes:data.bytes length:data.length];
block(YES); block(YES);
} else { } else {
GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error); GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", self->_socket, strerror(error), error);
block(NO); block(NO);
} }
} }
}); });
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
dispatch_release(buffer); dispatch_release(buffer);
@@ -622,15 +600,14 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block { - (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
GWS_DCHECK([_response hasBody]); GWS_DCHECK([_response hasBody]);
[_response performReadDataWithCompletion:^(NSData* data, NSError* error) { [_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
if (data) { if (data) {
if (data.length) { if (data.length) {
if (_response.usesChunkedTransferEncoding) { if (self->_response.usesChunkedTransferEncoding) {
const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String]; const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
size_t hexLength = strlen(hexString); size_t hexLength = strlen(hexString);
NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)]; NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
if (chunk == nil) { if (chunk == nil) {
GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error); GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", self->_socket, error);
block(NO); block(NO);
return; return;
} }
@@ -647,31 +624,26 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
} }
[self writeData:data [self writeData:data
withCompletionBlock:^(BOOL success) { withCompletionBlock:^(BOOL success) {
if (success) { if (success) {
[self writeBodyWithCompletionBlock:block]; [self writeBodyWithCompletionBlock:block];
} else { } else {
block(NO); block(NO);
} }
}]; }];
} else { } else {
if (_response.usesChunkedTransferEncoding) { if (self->_response.usesChunkedTransferEncoding) {
[self writeData:_lastChunkData [self writeData:_lastChunkData
withCompletionBlock:^(BOOL success) { withCompletionBlock:^(BOOL success) {
block(success); block(success);
}]; }];
} else { } else {
block(YES); block(YES);
} }
} }
} else { } else {
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error); GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", self->_socket, error);
block(NO); block(NO);
} }
}]; }];
} }
@@ -726,7 +698,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
#endif #endif
} }
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers { - (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary<NSString*, NSString*>*)headers {
return url; return url;
} }
@@ -822,8 +794,8 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
GWS_DCHECK(_responseMessage == NULL); GWS_DCHECK(_responseMessage == NULL);
GWS_DCHECK((statusCode >= 400) && (statusCode < 600)); GWS_DCHECK((statusCode >= 400) && (statusCode < 600));
[self _initializeResponseHeadersWithStatusCode:statusCode]; [self _initializeResponseHeadersWithStatusCode:statusCode];
[self writeHeadersWithCompletionBlock:^(BOOL success) { [self writeHeadersWithCompletionBlock:^(BOOL success){
; // Nothing more to do // Nothing more to do
}]; }];
GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket); GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket);
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,7 @@ extern "C" {
* types. Keys of the dictionary must be lowercased file extensions without * types. Keys of the dictionary must be lowercased file extensions without
* the period, and the values must be the corresponding MIME types. * the period, and the values must be the corresponding MIME types.
*/ */
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* _Nullable overrides); NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary<NSString*, NSString*>* _Nullable overrides);
/** /**
* Add percent-escapes to a string so it can be used in a URL. * Add percent-escapes to a string so it can be used in a URL.
@@ -60,7 +60,7 @@ NSString* _Nullable GCDWebServerUnescapeURLString(NSString* string);
* "application/x-www-form-urlencoded" form. * "application/x-www-form-urlencoded" form.
* http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 * http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
*/ */
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); NSDictionary<NSString*, NSString*>* GCDWebServerParseURLEncodedForm(NSString* form);
/** /**
* On OS X, returns the IPv4 or IPv6 address as a string of the primary * On OS X, returns the IPv4 or IPv6 address as a string of the primary
@@ -102,6 +102,11 @@ NSString* GCDWebServerFormatISO8601(NSDate* date);
*/ */
NSDate* _Nullable GCDWebServerParseISO8601(NSString* string); NSDate* _Nullable GCDWebServerParseISO8601(NSString* string);
/**
* Removes "//", "/./" and "/../" components from path as well as any trailing slash.
*/
NSString* GCDWebServerNormalizePath(NSString* path);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@
#import <TargetConditionals.h> #import <TargetConditionals.h>
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
#import <MobileCoreServices/MobileCoreServices.h> #import <CoreServices/CoreServices.h>
#else #else
#import <SystemConfiguration/SystemConfiguration.h> #import <SystemConfiguration/SystemConfiguration.h>
#endif #endif
@@ -166,8 +166,8 @@ NSString* GCDWebServerDescribeData(NSData* data, NSString* type) {
return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length]; return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length];
} }
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* overrides) { NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary<NSString*, NSString*>* overrides) {
NSDictionary* builtInOverrides = @{ @"css" : @"text/css" }; NSDictionary* builtInOverrides = @{@"css" : @"text/css"};
NSString* mimeType = nil; NSString* mimeType = nil;
extension = [extension lowercaseString]; extension = [extension lowercaseString];
if (extension.length) { if (extension.length) {
@@ -200,7 +200,7 @@ NSString* GCDWebServerUnescapeURLString(NSString* string) {
#pragma clang diagnostic pop #pragma clang diagnostic pop
} }
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { NSDictionary<NSString*, NSString*>* GCDWebServerParseURLEncodedForm(NSString* form) {
NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
NSScanner* scanner = [[NSScanner alloc] initWithString:form]; NSScanner* scanner = [[NSScanner alloc] initWithString:form];
[scanner setCharactersToBeSkipped:nil]; [scanner setCharactersToBeSkipped:nil];
@@ -302,7 +302,10 @@ NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) {
const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String]; const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String];
va_end(arguments); va_end(arguments);
unsigned char md5[CC_MD5_DIGEST_LENGTH]; unsigned char md5[CC_MD5_DIGEST_LENGTH];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
CC_MD5(string, (CC_LONG)strlen(string), md5); CC_MD5(string, (CC_LONG)strlen(string), md5);
#pragma clang diagnostic pop
char buffer[2 * CC_MD5_DIGEST_LENGTH + 1]; char buffer[2 * CC_MD5_DIGEST_LENGTH + 1];
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) { for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) {
unsigned char byte = md5[i]; unsigned char byte = md5[i];
@@ -314,3 +317,18 @@ NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) {
buffer[2 * CC_MD5_DIGEST_LENGTH] = 0; buffer[2 * CC_MD5_DIGEST_LENGTH] = 0;
return (NSString*)[NSString stringWithUTF8String:buffer]; return (NSString*)[NSString stringWithUTF8String:buffer];
} }
NSString* GCDWebServerNormalizePath(NSString* path) {
NSMutableArray* components = [[NSMutableArray alloc] init];
for (NSString* component in [path componentsSeparatedByString:@"/"]) {
if ([component isEqualToString:@".."]) {
[components removeLastObject];
} else if (component.length && ![component isEqualToString:@"."]) {
[components addObject:component];
}
}
if (path.length && ([path characterAtIndex:0] == '/')) {
return [@"/" stringByAppendingString:[components componentsJoinedByString:@"/"]]; // Preserve initial slash
}
return [components componentsJoinedByString:@"/"];
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -48,8 +48,6 @@
#import "GCDWebServerFileResponse.h" #import "GCDWebServerFileResponse.h"
#import "GCDWebServerStreamedResponse.h" #import "GCDWebServerStreamedResponse.h"
NS_ASSUME_NONNULL_BEGIN
/** /**
* Check if a custom logging facility should be used instead. * Check if a custom logging facility should be used instead.
*/ */
@@ -101,7 +99,7 @@ typedef NS_ENUM(int, GCDWebServerLoggingLevel) {
}; };
extern GCDWebServerLoggingLevel GCDWebServerLogLevel; extern GCDWebServerLoggingLevel GCDWebServerLogLevel;
extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3); extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* _Nonnull format, ...) NS_FORMAT_FUNCTION(2, 3);
#if DEBUG #if DEBUG
#define GWS_LOG_DEBUG(...) \ #define GWS_LOG_DEBUG(...) \
@@ -155,6 +153,8 @@ extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* for
#endif #endif
NS_ASSUME_NONNULL_BEGIN
/** /**
* GCDWebServer internal constants and APIs. * GCDWebServer internal constants and APIs.
*/ */
@@ -170,7 +170,7 @@ static inline NSError* GCDWebServerMakePosixError(int code) {
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}]; return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}];
} }
extern void GCDWebServerInitializeFunctions(); extern void GCDWebServerInitializeFunctions(void);
extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value); extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value);
extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value); extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value);
extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute); extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute);
@@ -185,11 +185,11 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
@end @end
@interface GCDWebServer () @interface GCDWebServer ()
@property(nonatomic, readonly) NSMutableArray* handlers; @property(nonatomic, readonly) NSMutableArray<GCDWebServerHandler*>* handlers;
@property(nonatomic, readonly, nullable) NSString* serverName; @property(nonatomic, readonly, nullable) NSString* serverName;
@property(nonatomic, readonly, nullable) NSString* authenticationRealm; @property(nonatomic, readonly, nullable) NSString* authenticationRealm;
@property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationBasicAccounts; @property(nonatomic, readonly, nullable) NSMutableDictionary<NSString*, NSString*>* authenticationBasicAccounts;
@property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationDigestAccounts; @property(nonatomic, readonly, nullable) NSMutableDictionary<NSString*, NSString*>* authenticationDigestAccounts;
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET; @property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
@property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority; @property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority;
- (void)willStartConnection:(GCDWebServerConnection*)connection; - (void)willStartConnection:(GCDWebServerConnection*)connection;
@@ -213,7 +213,7 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
@end @end
@interface GCDWebServerResponse () @interface GCDWebServerResponse ()
@property(nonatomic, readonly) NSDictionary* additionalHeaders; @property(nonatomic, readonly) NSDictionary<NSString*, NSString*>* additionalHeaders;
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
- (void)prepareForReading; - (void)prepareForReading;
- (BOOL)performOpen:(NSError**)error; - (BOOL)performOpen:(NSError**)error;

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -102,7 +102,7 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
/** /**
* Returns the HTTP headers for the request. * Returns the HTTP headers for the request.
*/ */
@property(nonatomic, readonly) NSDictionary* headers; @property(nonatomic, readonly) NSDictionary<NSString*, NSString*>* headers;
/** /**
* Returns the path component of the URL for the request. * Returns the path component of the URL for the request.
@@ -114,7 +114,7 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
* *
* @warning This property will be nil if there is no query in the URL. * @warning This property will be nil if there is no query in the URL.
*/ */
@property(nonatomic, readonly, nullable) NSDictionary* query; @property(nonatomic, readonly, nullable) NSDictionary<NSString*, NSString*>* query;
/** /**
* Returns the content type for the body of the request parsed from the * Returns the content type for the body of the request parsed from the
@@ -186,7 +186,7 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
/** /**
* This method is the designated initializer for the class. * This method is the designated initializer for the class.
*/ */
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(nullable NSDictionary*)query; - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary<NSString*, NSString*>*)headers path:(NSString*)path query:(nullable NSDictionary<NSString*, NSString*>*)query;
/** /**
* Convenience method that checks if the contentType property is defined. * Convenience method that checks if the contentType property is defined.

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -136,12 +136,12 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
@implementation GCDWebServerRequest { @implementation GCDWebServerRequest {
BOOL _opened; BOOL _opened;
NSMutableArray* _decoders; NSMutableArray<GCDWebServerBodyDecoder*>* _decoders;
id<GCDWebServerBodyWriter> __unsafe_unretained _writer; id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
NSMutableDictionary* _attributes; NSMutableDictionary<NSString*, id>* _attributes;
} }
- (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<NSString*, NSString*>*)headers path:(NSString*)path query:(NSDictionary<NSString*, NSString*>*)query {
if ((self = [super init])) { if ((self = [super init])) {
_method = [method copy]; _method = [method copy];
_URL = url; _URL = url;
@@ -188,7 +188,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
if ([rangeHeader hasPrefix:@"bytes="]) { if ([rangeHeader hasPrefix:@"bytes="]) {
NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","]; NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","];
if (components.count == 1) { if (components.count == 1) {
components = [[components firstObject] componentsSeparatedByString:@"-"]; components = [(NSString*)[components firstObject] componentsSeparatedByString:@"-"];
if (components.count == 2) { if (components.count == 2) {
NSString* startString = [components objectAtIndex:0]; NSString* startString = [components objectAtIndex:0];
NSInteger startValue = [startString integerValue]; NSInteger startValue = [startString integerValue];

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -33,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
* The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the * The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the
* GCDWebServerBodyReader object when reading data from it asynchronously. * GCDWebServerBodyReader object when reading data from it asynchronously.
*/ */
typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* _Nullable error); typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* _Nullable data, NSError* _Nullable error);
/** /**
* This protocol is used by the GCDWebServerConnection to communicate with * This protocol is used by the GCDWebServerConnection to communicate with

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -150,12 +150,12 @@
@implementation GCDWebServerResponse { @implementation GCDWebServerResponse {
BOOL _opened; BOOL _opened;
NSMutableArray* _encoders; NSMutableArray<GCDWebServerBodyEncoder*>* _encoders;
id<GCDWebServerBodyReader> __unsafe_unretained _reader; id<GCDWebServerBodyReader> __unsafe_unretained _reader;
} }
+ (instancetype)response { + (instancetype)response {
return [[[self class] alloc] init]; return [(GCDWebServerResponse*)[[self class] alloc] init];
} }
- (instancetype)init { - (instancetype)init {
@@ -259,11 +259,11 @@
@implementation GCDWebServerResponse (Extensions) @implementation GCDWebServerResponse (Extensions)
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode { + (instancetype)responseWithStatusCode:(NSInteger)statusCode {
return [[self alloc] initWithStatusCode:statusCode]; return [(GCDWebServerResponse*)[self alloc] initWithStatusCode:statusCode];
} }
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent { + (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
return [[self alloc] initWithRedirect:location permanent:permanent]; return [(GCDWebServerResponse*)[self alloc] initWithRedirect:location permanent:permanent];
} }
- (instancetype)initWithStatusCode:(NSInteger)statusCode { - (instancetype)initWithStatusCode:(NSInteger)statusCode {

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -48,7 +48,7 @@
} }
if (_data == nil) { if (_data == nil) {
if (error) { if (error) {
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed allocating memory" }]; *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed allocating memory"}];
} }
return NO; return NO;
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -35,7 +35,7 @@
int _file; int _file;
} }
- (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<NSString*, NSString*>*)headers path:(NSString*)path query:(NSDictionary<NSString*, NSString*>*)query {
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
_temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; _temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -107,13 +107,13 @@ NS_ASSUME_NONNULL_BEGIN
* Returns the argument parts from the multipart encoded form as * Returns the argument parts from the multipart encoded form as
* name / GCDWebServerMultiPartArgument pairs. * name / GCDWebServerMultiPartArgument pairs.
*/ */
@property(nonatomic, readonly) NSArray* arguments; @property(nonatomic, readonly) NSArray<GCDWebServerMultiPartArgument*>* arguments;
/** /**
* Returns the files parts from the multipart encoded form as * Returns the files parts from the multipart encoded form as
* name / GCDWebServerMultiPartFile pairs. * name / GCDWebServerMultiPartFile pairs.
*/ */
@property(nonatomic, readonly) NSArray* files; @property(nonatomic, readonly) NSArray<GCDWebServerMultiPartFile*>* files;
/** /**
* Returns the MIME type for multipart encoded forms * Returns the MIME type for multipart encoded forms

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -106,8 +106,8 @@ static NSData* _dashNewlineData = nil;
NSString* _defaultcontrolName; NSString* _defaultcontrolName;
ParserState _state; ParserState _state;
NSMutableData* _data; NSMutableData* _data;
NSMutableArray* _arguments; NSMutableArray<GCDWebServerMultiPartArgument*>* _arguments;
NSMutableArray* _files; NSMutableArray<GCDWebServerMultiPartFile*>* _files;
NSString* _controlName; NSString* _controlName;
NSString* _fileName; NSString* _fileName;
@@ -132,7 +132,7 @@ static NSData* _dashNewlineData = nil;
} }
} }
- (instancetype)initWithBoundary:(NSString* _Nonnull)boundary defaultControlName:(NSString* _Nullable)name arguments:(NSMutableArray* _Nonnull)arguments files:(NSMutableArray* _Nonnull)files { - (instancetype)initWithBoundary:(NSString* _Nonnull)boundary defaultControlName:(NSString* _Nullable)name arguments:(NSMutableArray<GCDWebServerMultiPartArgument*>* _Nonnull)arguments files:(NSMutableArray<GCDWebServerMultiPartFile*>* _Nonnull)files {
NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil; NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil;
if (data == nil) { if (data == nil) {
GWS_DNOT_REACHED(); GWS_DNOT_REACHED();
@@ -312,8 +312,8 @@ static NSData* _dashNewlineData = nil;
@end @end
@interface GCDWebServerMultiPartFormRequest () @interface GCDWebServerMultiPartFormRequest ()
@property(nonatomic) NSMutableArray* arguments; @property(nonatomic) NSMutableArray<GCDWebServerMultiPartArgument*>* arguments;
@property(nonatomic) NSMutableArray* files; @property(nonatomic) NSMutableArray<GCDWebServerMultiPartFile*>* files;
@end @end
@implementation GCDWebServerMultiPartFormRequest { @implementation GCDWebServerMultiPartFormRequest {
@@ -324,7 +324,7 @@ static NSData* _dashNewlineData = nil;
return @"multipart/form-data"; return @"multipart/form-data";
} }
- (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<NSString*, NSString*>*)headers path:(NSString*)path query:(NSDictionary<NSString*, NSString*>*)query {
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
_arguments = [[NSMutableArray alloc] init]; _arguments = [[NSMutableArray alloc] init];
_files = [[NSMutableArray alloc] init]; _files = [[NSMutableArray alloc] init];
@@ -337,7 +337,7 @@ static NSData* _dashNewlineData = nil;
_parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files]; _parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files];
if (_parser == nil) { if (_parser == nil) {
if (error) { if (error) {
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed starting to parse multipart form data" }]; *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed starting to parse multipart form data"}];
} }
return NO; return NO;
} }
@@ -347,7 +347,7 @@ static NSData* _dashNewlineData = nil;
- (BOOL)writeData:(NSData*)data error:(NSError**)error { - (BOOL)writeData:(NSData*)data error:(NSError**)error {
if (![_parser appendBytes:data.bytes length:data.length]) { if (![_parser appendBytes:data.bytes length:data.length]) {
if (error) { if (error) {
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed continuing to parse multipart form data" }]; *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed continuing to parse multipart form data"}];
} }
return NO; return NO;
} }
@@ -359,7 +359,7 @@ static NSData* _dashNewlineData = nil;
_parser = nil; _parser = nil;
if (!atEnd) { if (!atEnd) {
if (error) { if (error) {
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed finishing to parse multipart form data" }]; *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed finishing to parse multipart form data"}];
} }
return NO; return NO;
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
* The text encoding used to interpret the data is extracted from the * The text encoding used to interpret the data is extracted from the
* "Content-Type" header or defaults to UTF-8. * "Content-Type" header or defaults to UTF-8.
*/ */
@property(nonatomic, readonly) NSDictionary* arguments; @property(nonatomic, readonly) NSDictionary<NSString*, NSString*>* arguments;
/** /**
* Returns the MIME type for URL encoded forms * Returns the MIME type for URL encoded forms

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -64,7 +64,7 @@ NS_ASSUME_NONNULL_BEGIN
* Creates a data response from an HTML template encoded using UTF-8. * Creates a data response from an HTML template encoded using UTF-8.
* See -initWithHTMLTemplate:variables: for details. * See -initWithHTMLTemplate:variables: for details.
*/ */
+ (nullable instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; + (nullable instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary<NSString*, NSString*>*)variables;
/** /**
* Creates a data response from a serialized JSON object and the default * Creates a data response from a serialized JSON object and the default
@@ -94,7 +94,7 @@ NS_ASSUME_NONNULL_BEGIN
* All occurences of "%variable%" within the HTML template are replaced with * All occurences of "%variable%" within the HTML template are replaced with
* their corresponding values. * their corresponding values.
*/ */
- (nullable instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; - (nullable instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary<NSString*, NSString*>*)variables;
/** /**
* Initializes a data response from a serialized JSON object and the default * Initializes a data response from a serialized JSON object and the default

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@
@dynamic contentType; @dynamic contentType;
+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type { + (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type {
return [[[self class] alloc] initWithData:data contentType:type]; return [(GCDWebServerDataResponse*)[[self class] alloc] initWithData:data contentType:type];
} }
- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type { - (instancetype)initWithData:(NSData*)data contentType:(NSString*)type {
@@ -75,23 +75,23 @@
@implementation GCDWebServerDataResponse (Extensions) @implementation GCDWebServerDataResponse (Extensions)
+ (instancetype)responseWithText:(NSString*)text { + (instancetype)responseWithText:(NSString*)text {
return [[self alloc] initWithText:text]; return [(GCDWebServerDataResponse*)[self alloc] initWithText:text];
} }
+ (instancetype)responseWithHTML:(NSString*)html { + (instancetype)responseWithHTML:(NSString*)html {
return [[self alloc] initWithHTML:html]; return [(GCDWebServerDataResponse*)[self alloc] initWithHTML:html];
} }
+ (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { + (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary<NSString*, NSString*>*)variables {
return [[self alloc] initWithHTMLTemplate:path variables:variables]; return [(GCDWebServerDataResponse*)[self alloc] initWithHTMLTemplate:path variables:variables];
} }
+ (instancetype)responseWithJSONObject:(id)object { + (instancetype)responseWithJSONObject:(id)object {
return [[self alloc] initWithJSONObject:object]; return [(GCDWebServerDataResponse*)[self alloc] initWithJSONObject:object];
} }
+ (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type { + (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type {
return [[self alloc] initWithJSONObject:object contentType:type]; return [(GCDWebServerDataResponse*)[self alloc] initWithJSONObject:object contentType:type];
} }
- (instancetype)initWithText:(NSString*)text { - (instancetype)initWithText:(NSString*)text {
@@ -112,7 +112,7 @@
return [self initWithData:data contentType:@"text/html; charset=utf-8"]; return [self initWithData:data contentType:@"text/html; charset=utf-8"];
} }
- (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { - (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary<NSString*, NSString*>*)variables {
NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
[variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) { [variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) {
[html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)]; [html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)];

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -37,7 +37,7 @@
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
va_list arguments; va_list arguments;
va_start(arguments, format); va_start(arguments, format);
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
va_end(arguments); va_end(arguments);
return response; return response;
} }
@@ -46,7 +46,7 @@
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
va_list arguments; va_list arguments;
va_start(arguments, format); va_start(arguments, format);
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
va_end(arguments); va_end(arguments);
return response; return response;
} }
@@ -55,7 +55,7 @@
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
va_list arguments; va_list arguments;
va_start(arguments, format); va_start(arguments, format);
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
va_end(arguments); va_end(arguments);
return response; return response;
} }
@@ -64,7 +64,7 @@
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
va_list arguments; va_list arguments;
va_start(arguments, format); va_start(arguments, format);
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
va_end(arguments); va_end(arguments);
return response; return response;
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -101,7 +101,7 @@ NS_ASSUME_NONNULL_BEGIN
* file extensions without the period, and the values must be the corresponding * file extensions without the period, and the values must be the corresponding
* MIME types. * MIME types.
*/ */
- (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(nullable NSDictionary*)overrides; - (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(nullable NSDictionary<NSString*, NSString*>*)overrides;
@end @end

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -45,19 +45,19 @@
@dynamic contentType, lastModifiedDate, eTag; @dynamic contentType, lastModifiedDate, eTag;
+ (instancetype)responseWithFile:(NSString*)path { + (instancetype)responseWithFile:(NSString*)path {
return [[[self class] alloc] initWithFile:path]; return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path];
} }
+ (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment { + (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment {
return [[[self class] alloc] initWithFile:path isAttachment:attachment]; return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path isAttachment:attachment];
} }
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range { + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range {
return [[[self class] alloc] initWithFile:path byteRange:range]; return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path byteRange:range];
} }
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment { + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment {
return [[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment mimeTypeOverrides:nil]; return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment mimeTypeOverrides:nil];
} }
- (instancetype)initWithFile:(NSString*)path { - (instancetype)initWithFile:(NSString*)path {
@@ -76,7 +76,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)]; return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)];
} }
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(NSDictionary*)overrides { - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(NSDictionary<NSString*, NSString*>*)overrides {
struct stat info; struct stat info;
if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) { if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) {
GWS_DNOT_REACHED(); GWS_DNOT_REACHED();

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -38,21 +38,19 @@
@dynamic contentType; @dynamic contentType;
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { + (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
return [[[self class] alloc] initWithContentType:type streamBlock:block]; return [(GCDWebServerStreamedResponse*)[[self class] alloc] initWithContentType:type streamBlock:block];
} }
+ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { + (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block {
return [[[self class] alloc] initWithContentType:type asyncStreamBlock:block]; return [(GCDWebServerStreamedResponse*)[[self class] alloc] initWithContentType:type asyncStreamBlock:block];
} }
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
return [self initWithContentType:type return [self initWithContentType:type
asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
NSError* error = nil; NSError* error = nil;
NSData* data = block(&error); NSData* data = block(&error);
completionBlock(data, error); completionBlock(data, error);
}]; }];
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
<!-- <!--
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -93,7 +93,7 @@ NS_ASSUME_NONNULL_BEGIN
* *
* The default value is nil i.e. all file extensions are allowed. * The default value is nil i.e. all file extensions are allowed.
*/ */
@property(nonatomic, copy) NSArray* allowedFileExtensions; @property(nonatomic, copy) NSArray<NSString*>* allowedFileExtensions;
/** /**
* Sets if files and directories whose name start with a period are allowed to * Sets if files and directories whose name start with a period are allowed to

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,7 @@
#endif #endif
#import "GCDWebUploader.h" #import "GCDWebUploader.h"
#import "GCDWebServerFunctions.h"
#import "GCDWebServerDataRequest.h" #import "GCDWebServerDataRequest.h"
#import "GCDWebServerMultiPartFormRequest.h" #import "GCDWebServerMultiPartFormRequest.h"
@@ -73,7 +74,7 @@ NS_ASSUME_NONNULL_END
if (siteBundle == nil) { if (siteBundle == nil) {
return nil; return nil;
} }
_uploadDirectory = [[path stringByStandardizingPath] copy]; _uploadDirectory = [path copy];
GCDWebUploader* __unsafe_unretained server = self; GCDWebUploader* __unsafe_unretained server = self;
// Resource files // Resource files
@@ -117,6 +118,9 @@ NS_ASSUME_NONNULL_END
NSString* footer = server.footer; NSString* footer = server.footer;
if (footer == nil) { if (footer == nil) {
NSString* name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; NSString* name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
if (name == nil) {
name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
}
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
#if !TARGET_OS_IPHONE #if !TARGET_OS_IPHONE
if (!name && !version) { if (!name && !version) {
@@ -135,7 +139,6 @@ NS_ASSUME_NONNULL_END
@"epilogue" : epilogue, @"epilogue" : epilogue,
@"footer" : footer @"footer" : footer
}]; }];
}]; }];
// File listing // File listing
@@ -193,11 +196,6 @@ NS_ASSUME_NONNULL_END
@implementation GCDWebUploader (Methods) @implementation GCDWebUploader (Methods)
// Must match implementation in GCDWebDAVServer
- (BOOL)_checkSandboxedPath:(NSString*)path {
return [[path stringByStandardizingPath] hasPrefix:_uploadDirectory];
}
- (BOOL)_checkFileExtension:(NSString*)fileName { - (BOOL)_checkFileExtension:(NSString*)fileName {
if (_allowedFileExtensions && ![_allowedFileExtensions containsObject:[[fileName pathExtension] lowercaseString]]) { if (_allowedFileExtensions && ![_allowedFileExtensions containsObject:[[fileName pathExtension] lowercaseString]]) {
return NO; return NO;
@@ -225,9 +223,9 @@ NS_ASSUME_NONNULL_END
- (GCDWebServerResponse*)listDirectory:(GCDWebServerRequest*)request { - (GCDWebServerResponse*)listDirectory:(GCDWebServerRequest*)request {
NSString* relativePath = [[request query] objectForKey:@"path"]; NSString* relativePath = [[request query] objectForKey:@"path"];
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { if (!absolutePath || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
} }
if (!isDirectory) { if (!isDirectory) {
@@ -240,7 +238,7 @@ NS_ASSUME_NONNULL_END
} }
NSError* error = nil; NSError* error = nil;
NSArray* contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error]; NSArray* contents = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
if (contents == nil) { if (contents == nil) {
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath]; return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
} }
@@ -269,9 +267,9 @@ NS_ASSUME_NONNULL_END
- (GCDWebServerResponse*)downloadFile:(GCDWebServerRequest*)request { - (GCDWebServerResponse*)downloadFile:(GCDWebServerRequest*)request {
NSString* relativePath = [[request query] objectForKey:@"path"]; NSString* relativePath = [[request query] objectForKey:@"path"];
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
} }
if (isDirectory) { if (isDirectory) {
@@ -300,10 +298,7 @@ NS_ASSUME_NONNULL_END
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName];
} }
NSString* relativePath = [[request firstArgumentForControlName:@"path"] string]; NSString* relativePath = [[request firstArgumentForControlName:@"path"] string];
NSString* absolutePath = [self _uniquePathForPath:[[_uploadDirectory stringByAppendingPathComponent:relativePath] stringByAppendingPathComponent:file.fileName]]; NSString* absolutePath = [self _uniquePathForPath:[[_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)] stringByAppendingPathComponent:file.fileName]];
if (![self _checkSandboxedPath:absolutePath]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
}
if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:file.temporaryPath]) { if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:file.temporaryPath]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file \"%@\" to \"%@\" is not permitted", file.fileName, relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file \"%@\" to \"%@\" is not permitted", file.fileName, relativePath];
@@ -324,21 +319,23 @@ NS_ASSUME_NONNULL_END
- (GCDWebServerResponse*)moveItem:(GCDWebServerURLEncodedFormRequest*)request { - (GCDWebServerResponse*)moveItem:(GCDWebServerURLEncodedFormRequest*)request {
NSString* oldRelativePath = [request.arguments objectForKey:@"oldPath"]; NSString* oldRelativePath = [request.arguments objectForKey:@"oldPath"];
NSString* oldAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:oldRelativePath]; NSString* oldAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(oldRelativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:oldAbsolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:oldAbsolutePath isDirectory:&isDirectory]) { if (![[NSFileManager defaultManager] fileExistsAtPath:oldAbsolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", oldRelativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", oldRelativePath];
} }
NSString* newRelativePath = [request.arguments objectForKey:@"newPath"]; NSString* oldItemName = [oldAbsolutePath lastPathComponent];
NSString* newAbsolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:newRelativePath]]; if ((!_allowHiddenItems && [oldItemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:oldItemName])) {
if (![self _checkSandboxedPath:newAbsolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving from item name \"%@\" is not allowed", oldItemName];
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", newRelativePath];
} }
NSString* itemName = [newAbsolutePath lastPathComponent]; NSString* newRelativePath = [request.arguments objectForKey:@"newPath"];
if ((!_allowHiddenItems && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) { NSString* newAbsolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(newRelativePath)]];
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving to item name \"%@\" is not allowed", itemName];
NSString* newItemName = [newAbsolutePath lastPathComponent];
if ((!_allowHiddenItems && [newItemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:newItemName])) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving to item name \"%@\" is not allowed", newItemName];
} }
if (![self shouldMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath]) { if (![self shouldMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath]) {
@@ -360,9 +357,9 @@ NS_ASSUME_NONNULL_END
- (GCDWebServerResponse*)deleteItem:(GCDWebServerURLEncodedFormRequest*)request { - (GCDWebServerResponse*)deleteItem:(GCDWebServerURLEncodedFormRequest*)request {
NSString* relativePath = [request.arguments objectForKey:@"path"]; NSString* relativePath = [request.arguments objectForKey:@"path"];
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
BOOL isDirectory = NO; BOOL isDirectory = NO;
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
} }
@@ -390,10 +387,7 @@ NS_ASSUME_NONNULL_END
- (GCDWebServerResponse*)createDirectory:(GCDWebServerURLEncodedFormRequest*)request { - (GCDWebServerResponse*)createDirectory:(GCDWebServerURLEncodedFormRequest*)request {
NSString* relativePath = [request.arguments objectForKey:@"path"]; NSString* relativePath = [request.arguments objectForKey:@"path"];
NSString* absolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:relativePath]]; NSString* absolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)]];
if (![self _checkSandboxedPath:absolutePath]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
}
NSString* directoryName = [absolutePath lastPathComponent]; NSString* directoryName = [absolutePath lastPathComponent];
if (!_allowHiddenItems && [directoryName hasPrefix:@"."]) { if (!_allowHiddenItems && [directoryName hasPrefix:@"."]) {

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -178,10 +178,10 @@ int main(int argc, const char* argv[]) {
recording = YES; recording = YES;
} else if (!strcmp(argv[i], "-root") && (i + 1 < argc)) { } else if (!strcmp(argv[i], "-root") && (i + 1 < argc)) {
++i; ++i;
rootDirectory = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:argv[i] length:strlen(argv[i])] stringByStandardizingPath]; rootDirectory = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:argv[i] length:strlen(argv[i])];
} 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])];
} else if (!strcmp(argv[i], "-authenticationMethod") && (i + 1 < argc)) { } else if (!strcmp(argv[i], "-authenticationMethod") && (i + 1 < argc)) {
++i; ++i;
authenticationMethod = [NSString stringWithUTF8String:argv[i]]; authenticationMethod = [NSString stringWithUTF8String:argv[i]];
@@ -206,7 +206,7 @@ int main(int argc, const char* argv[]) {
switch (mode) { switch (mode) {
// Simply serve contents of home directory // Simply serve contents of home directory
case kMode_WebServer: { case kMode_WebServer: {
fprintf(stdout, "Running in Web Server mode from \"%s\"", [rootDirectory UTF8String]); fprintf(stdout, "Running in Web Server mode from \"%s\"\n", [rootDirectory UTF8String]);
webServer = [[GCDWebServer alloc] init]; webServer = [[GCDWebServer alloc] init];
[webServer addGETHandlerForBasePath:@"/" directoryPath:rootDirectory indexFilename:nil cacheAge:0 allowRangeRequests:YES]; [webServer addGETHandlerForBasePath:@"/" directoryPath:rootDirectory indexFilename:nil cacheAge:0 allowRangeRequests:YES];
break; break;
@@ -214,27 +214,24 @@ int main(int argc, const char* argv[]) {
// Renders a HTML page // Renders a HTML page
case kMode_HTMLPage: { case kMode_HTMLPage: {
fprintf(stdout, "Running in HTML Page mode"); fprintf(stdout, "Running in HTML Page mode\n");
webServer = [[GCDWebServer alloc] init]; webServer = [[GCDWebServer alloc] init];
[webServer addDefaultHandlerForMethod:@"GET" [webServer addDefaultHandlerForMethod:@"GET"
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
return [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"]; return [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
}]; }];
break; break;
} }
// Implements an HTML form // Implements an HTML form
case kMode_HTMLForm: { case kMode_HTMLForm: {
fprintf(stdout, "Running in HTML Form mode"); fprintf(stdout, "Running in HTML Form mode\n");
webServer = [[GCDWebServer alloc] init]; webServer = [[GCDWebServer alloc] init];
[webServer addHandlerForMethod:@"GET" [webServer addHandlerForMethod:@"GET"
path:@"/" path:@"/"
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
NSString* html = @" \ NSString* html = @" \
<html><body> \ <html><body> \
<form name=\"input\" action=\"/\" method=\"post\" enctype=\"application/x-www-form-urlencoded\"> \ <form name=\"input\" action=\"/\" method=\"post\" enctype=\"application/x-www-form-urlencoded\"> \
@@ -244,24 +241,21 @@ int main(int argc, const char* argv[]) {
</body></html> \ </body></html> \
"; ";
return [GCDWebServerDataResponse responseWithHTML:html]; return [GCDWebServerDataResponse responseWithHTML:html];
}]; }];
[webServer addHandlerForMethod:@"POST" [webServer addHandlerForMethod:@"POST"
path:@"/" path:@"/"
requestClass:[GCDWebServerURLEncodedFormRequest class] requestClass:[GCDWebServerURLEncodedFormRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"]; NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"];
NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p></body></html>", value]; NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p></body></html>", value];
return [GCDWebServerDataResponse responseWithHTML:html]; return [GCDWebServerDataResponse responseWithHTML:html];
}]; }];
break; break;
} }
// Implements HTML file upload // Implements HTML file upload
case kMode_HTMLFileUpload: { case kMode_HTMLFileUpload: {
fprintf(stdout, "Running in HTML File Upload mode"); fprintf(stdout, "Running in HTML File Upload mode\n");
webServer = [[GCDWebServer alloc] init]; webServer = [[GCDWebServer alloc] init];
NSString* formHTML = @" \ NSString* formHTML = @" \
<form name=\"input\" action=\"/\" method=\"post\" enctype=\"multipart/form-data\"> \ <form name=\"input\" action=\"/\" method=\"post\" enctype=\"multipart/form-data\"> \
@@ -274,16 +268,13 @@ int main(int argc, const char* argv[]) {
path:@"/" path:@"/"
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
NSString* html = [NSString stringWithFormat:@"<html><body>%@</body></html>", formHTML]; NSString* html = [NSString stringWithFormat:@"<html><body>%@</body></html>", formHTML];
return [GCDWebServerDataResponse responseWithHTML:html]; return [GCDWebServerDataResponse responseWithHTML:html];
}]; }];
[webServer addHandlerForMethod:@"POST" [webServer addHandlerForMethod:@"POST"
path:@"/" path:@"/"
requestClass:[GCDWebServerMultiPartFormRequest class] requestClass:[GCDWebServerMultiPartFormRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
NSMutableString* string = [NSMutableString string]; NSMutableString* string = [NSMutableString string];
for (GCDWebServerMultiPartArgument* argument in [(GCDWebServerMultiPartFormRequest*)request arguments]) { for (GCDWebServerMultiPartArgument* argument in [(GCDWebServerMultiPartFormRequest*)request arguments]) {
[string appendFormat:@"%@ = %@<br>", argument.controlName, argument.string]; [string appendFormat:@"%@ = %@<br>", argument.controlName, argument.string];
@@ -296,108 +287,87 @@ int main(int argc, const char* argv[]) {
}; };
NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p><hr>%@</body></html>", string, formHTML]; NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p><hr>%@</body></html>", string, formHTML];
return [GCDWebServerDataResponse responseWithHTML:html]; return [GCDWebServerDataResponse responseWithHTML:html];
}]; }];
break; break;
} }
// Serve home directory through WebDAV // Serve home directory through WebDAV
case kMode_WebDAV: { case kMode_WebDAV: {
fprintf(stdout, "Running in WebDAV mode from \"%s\"", [rootDirectory UTF8String]); fprintf(stdout, "Running in WebDAV mode from \"%s\"\n", [rootDirectory UTF8String]);
webServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:rootDirectory]; webServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:rootDirectory];
break; break;
} }
// Serve home directory through web uploader // Serve home directory through web uploader
case kMode_WebUploader: { case kMode_WebUploader: {
fprintf(stdout, "Running in Web Uploader mode from \"%s\"", [rootDirectory UTF8String]); fprintf(stdout, "Running in Web Uploader mode from \"%s\"\n", [rootDirectory UTF8String]);
webServer = [[GCDWebUploader alloc] initWithUploadDirectory:rootDirectory]; webServer = [[GCDWebUploader alloc] initWithUploadDirectory:rootDirectory];
break; break;
} }
// Test streaming responses // Test streaming responses
case kMode_StreamingResponse: { case kMode_StreamingResponse: {
fprintf(stdout, "Running in Streaming Response mode"); fprintf(stdout, "Running in Streaming Response mode\n");
webServer = [[GCDWebServer alloc] init]; webServer = [[GCDWebServer alloc] init];
[webServer addHandlerForMethod:@"GET" [webServer addHandlerForMethod:@"GET"
path:@"/sync" path:@"/sync"
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
__block int countDown = 10; __block int countDown = 10;
return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain"
streamBlock:^NSData*(NSError** error) { streamBlock:^NSData*(NSError** error) {
usleep(100 * 1000); usleep(100 * 1000);
if (countDown) { if (countDown) {
return [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding]; return [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding];
} else { } else {
return [NSData data]; return [NSData data];
} }
}]; }];
}]; }];
[webServer addHandlerForMethod:@"GET" [webServer addHandlerForMethod:@"GET"
path:@"/async" path:@"/async"
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
__block int countDown = 10; __block int countDown = 10;
return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain"
asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data]; NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data];
completionBlock(data, nil); completionBlock(data, nil);
}); });
}]; }];
}]; }];
break; break;
} }
// Test async responses // Test async responses
case kMode_AsyncResponse: { case kMode_AsyncResponse: {
fprintf(stdout, "Running in Async Response mode"); fprintf(stdout, "Running in Async Response mode\n");
webServer = [[GCDWebServer alloc] init]; webServer = [[GCDWebServer alloc] init];
[webServer addHandlerForMethod:@"GET" [webServer addHandlerForMethod:@"GET"
path:@"/async" path:@"/async"
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:(NSData*)[@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding] contentType:@"text/plain"]; GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:(NSData*)[@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding] contentType:@"text/plain"];
completionBlock(response); completionBlock(response);
}); });
}]; }];
[webServer addHandlerForMethod:@"GET" [webServer addHandlerForMethod:@"GET"
path:@"/async2" path:@"/async2"
requestClass:[GCDWebServerRequest class] requestClass:[GCDWebServerRequest class]
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock handlerCompletionBlock) { asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock handlerCompletionBlock) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__block int countDown = 10; __block int countDown = 10;
GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/plain"
asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock readerCompletionBlock) { asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock readerCompletionBlock) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data]; NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data];
readerCompletionBlock(data, nil); readerCompletionBlock(data, nil);
}); });
}]; }];
handlerCompletionBlock(response); handlerCompletionBlock(response);
}); });
}]; }];
break; break;
} }
@@ -410,7 +380,7 @@ int main(int argc, const char* argv[]) {
webServer.delegate = delegate; webServer.delegate = delegate;
#endif #endif
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]); fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
result = (int)[webServer runTestsWithOptions:@{ GCDWebServerOption_Port : @8080 } inDirectory:testDirectory]; result = (int)[webServer runTestsWithOptions:@{GCDWebServerOption_Port : @8080} inDirectory:testDirectory];
} else { } else {
webServer.delegate = delegate; webServer.delegate = delegate;
if (recording) { if (recording) {

View File

@@ -2,11 +2,11 @@ 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)
[![Version](http://cocoapod-badges.herokuapp.com/v/GCDWebServer/badge.png)](http://cocoadocs.org/docsets/GCDWebServer) [![Version](http://cocoapod-badges.herokuapp.com/v/GCDWebServer/badge.png)](https://cocoapods.org/pods/GCDWebServer)
[![Platform](http://cocoapod-badges.herokuapp.com/p/GCDWebServer/badge.png)](https://github.com/swisspol/GCDWebServer) [![Platform](http://cocoapod-badges.herokuapp.com/p/GCDWebServer/badge.png)](https://github.com/swisspol/GCDWebServer)
[![License](http://img.shields.io/cocoapods/l/GCDWebServer.svg)](LICENSE) [![License](http://img.shields.io/cocoapods/l/GCDWebServer.svg)](LICENSE)
GCDWebServer is a modern and lightweight GCD based HTTP 1.1 server designed to be embedded in OS X & iOS apps. It was written from scratch with the following goals in mind: GCDWebServer is a modern and lightweight GCD based HTTP 1.1 server designed to be embedded in iOS, macOS & tvOS apps. It was written from scratch with the following goals in mind:
* Elegant and easy to use architecture with only 4 core classes: server, connection, request and response (see "Understanding GCDWebServer's Architecture" below) * Elegant and easy to use architecture with only 4 core classes: server, connection, request and response (see "Understanding GCDWebServer's Architecture" below)
* Well designed API with fully documented headers for easy integration and customization * Well designed API with fully documented headers for easy integration and customization
* Entirely built with an event-driven design using [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) for best performance and concurrency * Entirely built with an event-driven design using [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) for best performance and concurrency
@@ -28,23 +28,22 @@ Extra built-in features:
Included extensions: Included extensions:
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of ```GCDWebServer``` that implements an interface for uploading and downloading files 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 macOS Finder)
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
* HTTPS * HTTPS
Requirements: Requirements:
* OS X 10.7 or later (x86_64) * macOS 10.7 or later (x86_64)
* iOS 8.0 or later (armv7, armv7s or arm64) * iOS 8.0 or later (armv7, armv7s or arm64)
* ARC memory management only (if you need MRC support use GCDWebServer 3.1 and earlier) * tvOS 9.0 or later (arm64)
* ARC memory management only (if you need MRC support use GCDWebServer 3.1 or earlier)
Getting Started Getting Started
=============== ===============
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. 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. Finally link to `libz` (via Target > Build Phases > Link Binary With Libraries) and add `$(SDKROOT)/usr/include/libxml2` to your header search paths (via Target > Build Settings > HEADER_SEARCH_PATHS).
If you add the files directly then (1) link to `libz` (via Target > Build Phases > Link Binary With Libraries) and (2) add `$(SDKROOT)/usr/include/libxml2` to your header search paths (via Target > Build Settings > HEADER_SEARCH_PATHS).
Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Podfile: Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Podfile:
``` ```
@@ -82,7 +81,7 @@ These code snippets show how to implement a custom HTTP server that runs on port
**IMPORTANT:** If not using CocoaPods, be sure to add the `libz` shared system library to the Xcode target for your app. **IMPORTANT:** If not using CocoaPods, be sure to add the `libz` shared system library to the Xcode target for your app.
**OS X version (command line tool):** **macOS version (command line tool):**
```objectivec ```objectivec
#import "GCDWebServer.h" #import "GCDWebServer.h"
#import "GCDWebServerDataResponse.h" #import "GCDWebServerDataResponse.h"
@@ -148,7 +147,7 @@ int main(int argc, const char* argv[]) {
@end @end
``` ```
**OS X Swift version (command line tool):** **macOS Swift version (command line tool):**
***webServer.swift*** ***webServer.swift***
```swift ```swift
@@ -159,12 +158,12 @@ func initWebServer() {
let webServer = GCDWebServer() let webServer = GCDWebServer()
webServer.addDefaultHandlerForMethod("GET", requestClass: GCDWebServerRequest.self, processBlock: {request in webServer.addDefaultHandler(forMethod: "GET", request: GCDWebServerRequest.self, processBlock: {request in
return GCDWebServerDataResponse(HTML:"<html><body><p>Hello World</p></body></html>") return GCDWebServerDataResponse(html:"<html><body><p>Hello World</p></body></html>")
})
}) webServer.start(withPort: 8080, bonjourName: "GCD Web Server")
webServer.runWithPort(8080, bonjourName: "GCD Web Server")
print("Visit \(webServer.serverURL) in your web browser") print("Visit \(webServer.serverURL) in your web browser")
} }
@@ -209,7 +208,7 @@ 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 [macOS Finder](http://support.apple.com/kb/PH13859) as it is partially class 2 compliant (but only when the client is the macOS WebDAV implementation).
Simply instantiate and run a ```GCDWebDAVServer``` instance then connect to ```http://{YOUR-IOS-DEVICE-IP-ADDRESS}/``` using a WebDAV client: Simply instantiate and run a ```GCDWebDAVServer``` instance then connect to ```http://{YOUR-IOS-DEVICE-IP-ADDRESS}/``` using a WebDAV client:
@@ -239,7 +238,7 @@ 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"](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) 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):
**OS X version (command line tool):** **macOS version (command line tool):**
```objectivec ```objectivec
#import "GCDWebServer.h" #import "GCDWebServer.h"
@@ -350,8 +349,8 @@ 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. 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: 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. - GCDWebServer begins a [background task](https://developer.apple.com/library/archive/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.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). - While the app is in the background, as long as new HTTP connections keep being initiated, the background task will continue to exist and iOS will not suspend the app **for up to 10 minutes** (unless under sudden and unexpected memory pressure).
- If the app is still in the background when the last HTTP connection is closed, GCDWebServer will suspend itself and stop accepting new connections as if you had called ```-stop``` (this behavior can be disabled with the ```GCDWebServerOption_AutomaticallySuspendInBackground``` option). - If the app 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 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```. - 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```.

View File

@@ -1,4 +1,10 @@
#!/bin/bash -ex #!/bin/bash -exu -o pipefail
if [[ -f "/usr/local/bin/xcpretty" ]]; then
PRETTYFIER="xcpretty"
else
PRETTYFIER="tee" # Passthrough stdout
fi
OSX_SDK="macosx" OSX_SDK="macosx"
IOS_SDK="iphonesimulator" IOS_SDK="iphonesimulator"
@@ -15,23 +21,28 @@ CONFIGURATION="Release"
OSX_TEST_SCHEME="GCDWebServers (Mac)" OSX_TEST_SCHEME="GCDWebServers (Mac)"
BUILD_DIR="/tmp/GCDWebServer-Build" BUILD_DIR="`pwd`/build"
PRODUCT="$BUILD_DIR/$CONFIGURATION/GCDWebServer" PRODUCT="$BUILD_DIR/$CONFIGURATION/GCDWebServer"
PAYLOAD_ZIP="Tests/Payload.zip" PAYLOAD_ZIP="Tests/Payload.zip"
PAYLOAD_DIR="/tmp/GCDWebServer-Payload" PAYLOAD_DIR="`pwd`/build/Payload"
function runTests { function runTests {
EXECUTABLE="$1"
MODE="$2"
TESTS="$3"
FILE="${4:-}"
rm -rf "$PAYLOAD_DIR" rm -rf "$PAYLOAD_DIR"
ditto -x -k "$PAYLOAD_ZIP" "$PAYLOAD_DIR" ditto -x -k "$PAYLOAD_ZIP" "$PAYLOAD_DIR"
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 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
if [ "$4" != "" ]; then if [ "$FILE" != "" ]; then
cp -f "$4" "$PAYLOAD_DIR/Payload" cp -f "$4" "$PAYLOAD_DIR/Payload"
pushd "$PAYLOAD_DIR/Payload" pushd "$PAYLOAD_DIR/Payload"
TZ=GMT SetFile -d "1/1/2014 00:00:00" -m "1/1/2014 00:00:00" `basename "$4"` TZ=GMT SetFile -d "1/1/2014 00:00:00" -m "1/1/2014 00:00:00" `basename "$FILE"`
popd popd
fi fi
logLevel=2 $1 -mode "$2" -root "$PAYLOAD_DIR/Payload" -tests "$3" logLevel=2 $EXECUTABLE -mode "$MODE" -root "$PAYLOAD_DIR/Payload" -tests "$TESTS"
} }
# Run built-in OS X tests # Run built-in OS X tests
@@ -40,7 +51,7 @@ xcodebuild test -scheme "$OSX_TEST_SCHEME" "SYMROOT=$BUILD_DIR"
# Build for OS X for oldest supported deployment target # Build for OS X for oldest supported deployment target
rm -rf "$BUILD_DIR" rm -rf "$BUILD_DIR"
xcodebuild build -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=10.7" > /dev/null xcodebuild build -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=10.7" | $PRETTYFIER
# Run tests # Run tests
runTests $PRODUCT "htmlForm" "Tests/HTMLForm" runTests $PRODUCT "htmlForm" "Tests/HTMLForm"
@@ -54,19 +65,19 @@ runTests $PRODUCT "webServer" "Tests/WebServer-Sample-Movie" "Tests/Sample-Movie
# Build for OS X for current deployment target # Build for OS X for current deployment target
rm -rf "$BUILD_DIR" rm -rf "$BUILD_DIR"
xcodebuild build -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=$OSX_SDK_VERSION" > /dev/null xcodebuild build -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=$OSX_SDK_VERSION" | $PRETTYFIER
# Build for iOS for oldest supported deployment target # Build for iOS for oldest supported deployment target
rm -rf "$BUILD_DIR" rm -rf "$BUILD_DIR"
xcodebuild build -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "IPHONEOS_DEPLOYMENT_TARGET=8.0" > /dev/null xcodebuild build -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "IPHONEOS_DEPLOYMENT_TARGET=8.0" | $PRETTYFIER
# Build for iOS for current deployment target # Build for iOS for current deployment target
rm -rf "$BUILD_DIR" rm -rf "$BUILD_DIR"
xcodebuild build -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "IPHONEOS_DEPLOYMENT_TARGET=$IOS_SDK_VERSION" > /dev/null xcodebuild build -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "IPHONEOS_DEPLOYMENT_TARGET=$IOS_SDK_VERSION" | $PRETTYFIER
# Build for tvOS for current deployment target # Build for tvOS for current deployment target
rm -rf "$BUILD_DIR" rm -rf "$BUILD_DIR"
xcodebuild build -sdk "$TVOS_SDK" -target "$TVOS_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "TVOS_DEPLOYMENT_TARGET=$TVOS_SDK_VERSION" > /dev/null xcodebuild build -sdk "$TVOS_SDK" -target "$TVOS_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "TVOS_DEPLOYMENT_TARGET=$TVOS_SDK_VERSION" | $PRETTYFIER
# Done # Done
echo "\nAll tests completed successfully!" echo "\nAll tests completed successfully!"

View File

@@ -1,13 +1,22 @@
#!/bin/sh -ex #!/bin/sh -exuo pipefail
# brew install clang-format # brew install clang-format
SWIFT_FORMAT_VERSION='0.44.5'
CLANG_FORMAT_VERSION=`clang-format -version | awk '{ print $3 }'` CLANG_FORMAT_VERSION=`clang-format -version | awk '{ print $3 }'`
if [[ "$CLANG_FORMAT_VERSION" != "5.0.0" ]]; then if [[ "$CLANG_FORMAT_VERSION" != "9.0.0" ]]; then
echo "Unsupported clang-format version" echo "Unsupported clang-format version"
exit 1 exit 1
fi fi
if [[ ! -f "build/swiftformat" ]]; then
mkdir -p "build"
curl -sfL -o "build/SwiftFormat.zip" "https://github.com/nicklockwood/SwiftFormat/archive/$SWIFT_FORMAT_VERSION.zip"
unzip "build/SwiftFormat.zip" "SwiftFormat-$SWIFT_FORMAT_VERSION/CommandLineTool/swiftformat" -d "build"
mv "build/SwiftFormat-$SWIFT_FORMAT_VERSION/CommandLineTool/swiftformat" "build/swiftformat"
fi
pushd "GCDWebServer/Core" pushd "GCDWebServer/Core"
clang-format -style=file -i *.h *.m clang-format -style=file -i *.h *.m
popd popd
@@ -30,11 +39,7 @@ popd
pushd "Mac" pushd "Mac"
clang-format -style=file -i *.m clang-format -style=file -i *.m
popd popd
pushd "iOS"
clang-format -style=file -i *.h *.m build/swiftformat --indent 2 "iOS" "tvOS"
popd
pushd "tvOS"
clang-format -style=file -i *.h *.m
popd
echo "OK" echo "OK"

View File

@@ -1,32 +0,0 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property(strong, nonatomic) UIWindow* window;
@end

View File

@@ -1,36 +0,0 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return YES;
}
@end

View File

@@ -1,7 +1,7 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
@@ -12,7 +12,7 @@
* The name of Pierre-Olivier Latour may not be used to endorse * The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific or promote products derived from this software without specific
prior written permission. prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -25,7 +25,9 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#import <UIKit/UIKit.h> import UIKit
@interface ViewController : UIViewController @UIApplicationMain
@end class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}

View File

@@ -84,6 +84,11 @@
"idiom" : "ipad", "idiom" : "ipad",
"size" : "83.5x83.5", "size" : "83.5x83.5",
"scale" : "2x" "scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
} }
], ],
"info" : { "info" : {

View File

@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9059" systemVersion="14F1021" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--View Controller--> <!--View Controller-->
@@ -10,14 +14,13 @@
<objects> <objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController"> <viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides> <layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/> <viewControllerLayoutGuide type="top" id="2rF-0L-CS8"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/> <viewControllerLayoutGuide type="bottom" id="kyj-O7-82f"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view> </view>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>

View File

@@ -1,41 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9059" systemVersion="14F1021" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--View Controller--> <!--View Controller-->
<scene sceneID="tne-QT-ifu"> <scene sceneID="tne-QT-ifu">
<objects> <objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController"> <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="GCDWebServer" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides> <layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> <viewControllerLayoutGuide type="top" id="dzM-0Q-5lj"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> <viewControllerLayoutGuide type="bottom" id="NtI-um-tgL"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fqi-2H-Bq5"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jIj-j0-ef8">
<rect key="frame" x="279" y="290" width="42" height="21"/> <rect key="frame" x="50" y="313" width="275" height="41"/>
<string key="text">Label
Label</string>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="fqi-2H-Bq5" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="fQm-a5-p9Z"/> <constraint firstAttribute="trailing" secondItem="jIj-j0-ef8" secondAttribute="trailing" constant="50" id="3B8-fm-R8n"/>
<constraint firstItem="fqi-2H-Bq5" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="vB0-cp-Fhd"/> <constraint firstItem="jIj-j0-ef8" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="CPv-7h-UCM"/>
<constraint firstItem="jIj-j0-ef8" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" constant="50" id="U0S-p4-n8S"/>
</constraints> </constraints>
</view> </view>
<connections> <connections>
<outlet property="label" destination="fqi-2H-Bq5" id="maJ-eb-cCq"/> <outlet property="label" destination="jIj-j0-ef8" id="6Lh-Oa-nCp"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="53.600000000000001" y="27.436281859070466"/>
</scene> </scene>
</scenes> </scenes>
</document> </document>

View File

@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>net.pol-online.${PRODUCT_NAME:rfc1034identifier}</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>

View File

@@ -1,77 +0,0 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ViewController.h"
#import "GCDWebUploader.h"
@interface ViewController () <GCDWebUploaderDelegate>
@property(weak, nonatomic) IBOutlet UILabel* label;
@end
@implementation ViewController {
@private
GCDWebUploader* _webServer;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
_webServer.delegate = self;
_webServer.allowHiddenItems = YES;
if ([_webServer start]) {
_label.text = [NSString stringWithFormat:NSLocalizedString(@"GCDWebServer running locally on port %i", nil), (int)_webServer.port];
} else {
_label.text = NSLocalizedString(@"GCDWebServer not running!", nil);
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[_webServer stop];
_webServer = nil;
}
- (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path {
NSLog(@"[UPLOAD] %@", path);
}
- (void)webUploader:(GCDWebUploader*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
NSLog(@"[MOVE] %@ -> %@", fromPath, toPath);
}
- (void)webUploader:(GCDWebUploader*)uploader didDeleteItemAtPath:(NSString*)path {
NSLog(@"[DELETE] %@", path);
}
- (void)webUploader:(GCDWebUploader*)uploader didCreateDirectoryAtPath:(NSString*)path {
NSLog(@"[CREATE] %@", path);
}
@end

77
iOS/ViewController.swift Normal file
View File

@@ -0,0 +1,77 @@
/*
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import GCDWebServers
import UIKit
class ViewController: UIViewController {
@IBOutlet var label: UILabel?
var webServer: GCDWebUploader!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
webServer = GCDWebUploader(uploadDirectory: documentsPath)
webServer.delegate = self
webServer.allowHiddenItems = true
if webServer.start() {
label?.text = "GCDWebServer running locally on port \(webServer.port)"
} else {
label?.text = "GCDWebServer not running!"
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
webServer.stop()
webServer = nil
}
}
extension ViewController: GCDWebUploaderDelegate {
func webUploader(_: GCDWebUploader, didUploadFileAtPath path: String) {
print("[UPLOAD] \(path)")
}
func webUploader(_: GCDWebUploader, didDownloadFileAtPath path: String) {
print("[DOWNLOAD] \(path)")
}
func webUploader(_: GCDWebUploader, didMoveItemFromPath fromPath: String, toPath: String) {
print("[MOVE] \(fromPath) -> \(toPath)")
}
func webUploader(_: GCDWebUploader, didCreateDirectoryAtPath path: String) {
print("[CREATE] \(path)")
}
func webUploader(_: GCDWebUploader, didDeleteItemAtPath path: String) {
print("[DELETE] \(path)")
}
}

View File

@@ -1,34 +0,0 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@@ -1,32 +0,0 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property(strong, nonatomic) UIWindow* window;
@end

View File

@@ -1,36 +0,0 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return YES;
}
@end

View File

@@ -1,7 +1,7 @@
/* /*
Copyright (c) 2012-2015, Pierre-Olivier Latour Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
@@ -12,7 +12,7 @@
* The name of Pierre-Olivier Latour may not be used to endorse * The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific or promote products derived from this software without specific
prior written permission. prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -25,7 +25,9 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#import <UIKit/UIKit.h> import UIKit
@interface ViewController : UIViewController @UIApplicationMain
@end class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}

View File

@@ -1,13 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="9059" systemVersion="14F1021" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" initialViewController="BYZ-38-t0r"> <document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="14460.31" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="appleTV" orientation="landscape">
<adaptation id="light"/>
</device>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/> <deployment identifier="tvOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--View Controller--> <!--View Controller-->
<scene sceneID="tne-QT-ifu"> <scene sceneID="tne-QT-ifu">
<objects> <objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController"> <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="GCDWebServer" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides> <layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
@@ -16,21 +21,22 @@
<rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/> <rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IHC-Pp-Jrx"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="V4U-rJ-6D1">
<rect key="frame" x="939" y="530" width="42" height="21"/> <rect key="frame" x="100" y="517" width="1720" height="46"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="calibratedWhite"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="IHC-Pp-Jrx" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="BOG-hA-JgS"/> <constraint firstItem="V4U-rJ-6D1" firstAttribute="leading" relation="lessThanOrEqual" secondItem="8bC-Xf-vdC" secondAttribute="leading" constant="100" id="Ljd-Mz-k93"/>
<constraint firstItem="IHC-Pp-Jrx" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="a9B-4C-wVj"/> <constraint firstItem="V4U-rJ-6D1" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="ZcT-KC-osO"/>
<constraint firstAttribute="trailing" relation="lessThanOrEqual" secondItem="V4U-rJ-6D1" secondAttribute="trailing" constant="100" id="ibV-Ar-n9i"/>
</constraints> </constraints>
</view> </view>
<connections> <connections>
<outlet property="label" destination="IHC-Pp-Jrx" id="lnE-JP-l00"/> <outlet property="label" destination="V4U-rJ-6D1" id="wPM-aF-IlM"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>

View File

@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>net.pol-online.${PRODUCT_NAME:rfc1034identifier}</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>

View File

@@ -1,77 +0,0 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ViewController.h"
#import "GCDWebUploader.h"
@interface ViewController () <GCDWebUploaderDelegate>
@property(weak, nonatomic) IBOutlet UILabel* label;
@end
@implementation ViewController {
@private
GCDWebUploader* _webServer;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
_webServer.delegate = self;
_webServer.allowHiddenItems = YES;
if ([_webServer start]) {
_label.text = [NSString stringWithFormat:NSLocalizedString(@"GCDWebServer running locally on port %i", nil), (int)_webServer.port];
} else {
_label.text = NSLocalizedString(@"GCDWebServer not running!", nil);
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[_webServer stop];
_webServer = nil;
}
- (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path {
NSLog(@"[UPLOAD] %@", path);
}
- (void)webUploader:(GCDWebUploader*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
NSLog(@"[MOVE] %@ -> %@", fromPath, toPath);
}
- (void)webUploader:(GCDWebUploader*)uploader didDeleteItemAtPath:(NSString*)path {
NSLog(@"[DELETE] %@", path);
}
- (void)webUploader:(GCDWebUploader*)uploader didCreateDirectoryAtPath:(NSString*)path {
NSLog(@"[CREATE] %@", path);
}
@end

77
tvOS/ViewController.swift Normal file
View File

@@ -0,0 +1,77 @@
/*
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import GCDWebServers
import UIKit
class ViewController: UIViewController {
@IBOutlet var label: UILabel?
var webServer: GCDWebUploader!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
webServer = GCDWebUploader(uploadDirectory: documentsPath)
webServer.delegate = self
webServer.allowHiddenItems = true
if webServer.start() {
label?.text = "GCDWebServer running locally on port \(webServer.port)"
} else {
label?.text = "GCDWebServer not running!"
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
webServer.stop()
webServer = nil
}
}
extension ViewController: GCDWebUploaderDelegate {
func webUploader(_: GCDWebUploader, didUploadFileAtPath path: String) {
print("[UPLOAD] \(path)")
}
func webUploader(_: GCDWebUploader, didDownloadFileAtPath path: String) {
print("[DOWNLOAD] \(path)")
}
func webUploader(_: GCDWebUploader, didMoveItemFromPath fromPath: String, toPath: String) {
print("[MOVE] \(fromPath) -> \(toPath)")
}
func webUploader(_: GCDWebUploader, didCreateDirectoryAtPath path: String) {
print("[CREATE] \(path)")
}
func webUploader(_: GCDWebUploader, didDeleteItemAtPath path: String) {
print("[DELETE] \(path)")
}
}

View File

@@ -1,34 +0,0 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The name of Pierre-Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}