mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-02-11 00:00:07 +08:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c98941121a | ||
|
|
4ee9c30911 | ||
|
|
48cf20bb55 | ||
|
|
ac9b8a5f47 | ||
|
|
cad428ca6f | ||
|
|
bb5c1a5195 | ||
|
|
bef95231d2 | ||
|
|
0192c364b6 | ||
|
|
21d9fc2f62 | ||
|
|
614ff58be5 | ||
|
|
7b0477b1e0 | ||
|
|
a674614713 | ||
|
|
b549f1197d | ||
|
|
9d38bb4f94 | ||
|
|
44c6a8adcf | ||
|
|
aaf8679308 | ||
|
|
81d74b46b8 | ||
|
|
f7de5cac09 | ||
|
|
a1c68352a4 | ||
|
|
e70a3338a5 | ||
|
|
3c33e9f056 | ||
|
|
d160e5ff91 | ||
|
|
2d2343ab34 | ||
|
|
f6783daadd | ||
|
|
99cae36644 | ||
|
|
b292710102 | ||
|
|
b8b4a35178 | ||
|
|
ecc572a934 | ||
|
|
3a02341b0c | ||
|
|
e792fe8eb6 | ||
|
|
4c8ec1d685 | ||
|
|
f7bb5babf8 | ||
|
|
ae88198f20 | ||
|
|
d71c0d493f | ||
|
|
d611ae0cbe | ||
|
|
93287edfd5 | ||
|
|
dc287906d6 | ||
|
|
ab9459a67a | ||
|
|
aa8fc97b9b | ||
|
|
863febed62 | ||
|
|
2ff117dbf3 | ||
|
|
4838d0def9 | ||
|
|
c394ae8bf5 | ||
|
|
bdfe6728ae | ||
|
|
b1ab7479b3 | ||
|
|
03a0ac32ee | ||
|
|
bd2c292cb6 | ||
|
|
e8b67264ab | ||
|
|
3d5fd0b828 | ||
|
|
9524d31b1b | ||
|
|
a3606d6027 | ||
|
|
00b2c38109 | ||
|
|
0a9d3105fc | ||
|
|
0f0a9840e4 | ||
|
|
047fdddb0e | ||
|
|
79d6075a84 | ||
|
|
594497d234 | ||
|
|
1f7c0366f0 | ||
|
|
fe472cdd54 | ||
|
|
9c33c83351 | ||
|
|
9719406303 | ||
|
|
0b8f7ff6ad | ||
|
|
71c08cff73 | ||
|
|
1a6786488a | ||
|
|
472c7855a7 | ||
|
|
2fdeb9581c | ||
|
|
c4310fcdf4 | ||
|
|
33645d3c6b | ||
|
|
3618dcac7e | ||
|
|
432e3826c9 | ||
|
|
4e31508195 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ xcuserdata
|
|||||||
project.xcworkspace
|
project.xcworkspace
|
||||||
|
|
||||||
Tests/Payload
|
Tests/Payload
|
||||||
|
Carthage/Build
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
language: objective-c
|
language: objective-c
|
||||||
script: ./Run-Tests.sh
|
script: ./Run-Tests.sh
|
||||||
|
osx_image: xcode7.1
|
||||||
|
|||||||
52
Frameworks/GCDWebServers.h
Normal file
52
Frameworks/GCDWebServers.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// GCDWebServer Core
|
||||||
|
#import <GCDWebServers/GCDWebServer.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerConnection.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerFunctions.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerHTTPStatusCodes.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerResponse.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerRequest.h>
|
||||||
|
|
||||||
|
// GCDWebServer Requests
|
||||||
|
#import <GCDWebServers/GCDWebServerDataRequest.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerFileRequest.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerMultiPartFormRequest.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerURLEncodedFormRequest.h>
|
||||||
|
|
||||||
|
// GCDWebServer Responses
|
||||||
|
#import <GCDWebServers/GCDWebServerDataResponse.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerErrorResponse.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerFileResponse.h>
|
||||||
|
#import <GCDWebServers/GCDWebServerStreamedResponse.h>
|
||||||
|
|
||||||
|
// GCDWebUploader
|
||||||
|
#import <GCDWebServers/GCDWebUploader.h>
|
||||||
|
|
||||||
|
// GCDWebDAVServer
|
||||||
|
#import <GCDWebServers/GCDWebDAVServer.h>
|
||||||
20
Frameworks/Info.plist
Normal file
20
Frameworks/Info.plist
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${BUNDLE_VERSION_STRING}</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
24
Frameworks/Tests.m
Normal file
24
Frameworks/Tests.m
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#import <GCDWebServers/GCDWebServers.h>
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
@interface Tests : XCTestCase
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation Tests
|
||||||
|
|
||||||
|
- (void)testWebServer {
|
||||||
|
GCDWebServer* server = [[GCDWebServer alloc] init];
|
||||||
|
XCTAssertNotNil(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDAVServer {
|
||||||
|
GCDWebDAVServer* server = [[GCDWebDAVServer alloc] init];
|
||||||
|
XCTAssertNotNil(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testWebUploader {
|
||||||
|
GCDWebUploader* server = [[GCDWebUploader alloc] init];
|
||||||
|
XCTAssertNotNil(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -261,7 +261,10 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
if ((dstRelativePath == nil) || (range.location == NSNotFound)) {
|
if ((dstRelativePath == nil) || (range.location == NSNotFound)) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Malformed 'Destination' header: %@", dstRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Malformed 'Destination' header: %@", dstRelativePath];
|
||||||
}
|
}
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#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
|
||||||
NSString* dstAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:dstRelativePath];
|
NSString* dstAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:dstRelativePath];
|
||||||
if (![self _checkSandboxedPath:dstAbsolutePath]) {
|
if (![self _checkSandboxedPath:dstAbsolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
|
||||||
@@ -333,7 +336,10 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)_addPropertyResponseForItem:(NSString*)itemPath resource:(NSString*)resourcePath properties:(DAVProperties)properties xmlString:(NSMutableString*)xmlString {
|
- (void)_addPropertyResponseForItem:(NSString*)itemPath resource:(NSString*)resourcePath properties:(DAVProperties)properties xmlString:(NSMutableString*)xmlString {
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
CFStringRef escapedPath = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)resourcePath, NULL, CFSTR("<&>?+"), kCFStringEncodingUTF8);
|
CFStringRef escapedPath = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)resourcePath, NULL, CFSTR("<&>?+"), kCFStringEncodingUTF8);
|
||||||
|
#pragma clang diagnostic pop
|
||||||
if (escapedPath) {
|
if (escapedPath) {
|
||||||
NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:itemPath error:NULL];
|
NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:itemPath error:NULL];
|
||||||
NSString* type = [attributes objectForKey:NSFileType];
|
NSString* type = [attributes objectForKey:NSFileType];
|
||||||
@@ -602,6 +608,8 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
|
|
||||||
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden;
|
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden;
|
||||||
|
|
||||||
|
@dynamic delegate;
|
||||||
|
|
||||||
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'GCDWebServer'
|
s.name = 'GCDWebServer'
|
||||||
s.version = '3.2.1'
|
s.version = '3.3'
|
||||||
s.author = { 'Pierre-Olivier Latour' => 'info@pol-online.net' }
|
s.author = { 'Pierre-Olivier Latour' => 'info@pol-online.net' }
|
||||||
s.license = { :type => 'BSD', :file => 'LICENSE' }
|
s.license = { :type => 'BSD', :file => 'LICENSE' }
|
||||||
s.homepage = 'https://github.com/swisspol/GCDWebServer'
|
s.homepage = 'https://github.com/swisspol/GCDWebServer'
|
||||||
@@ -15,6 +15,7 @@ Pod::Spec.new do |s|
|
|||||||
|
|
||||||
s.source = { :git => 'https://github.com/swisspol/GCDWebServer.git', :tag => s.version.to_s }
|
s.source = { :git => 'https://github.com/swisspol/GCDWebServer.git', :tag => s.version.to_s }
|
||||||
s.ios.deployment_target = '5.0'
|
s.ios.deployment_target = '5.0'
|
||||||
|
s.tvos.deployment_target = '9.0'
|
||||||
s.osx.deployment_target = '10.7'
|
s.osx.deployment_target = '10.7'
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
|
|
||||||
@@ -26,6 +27,8 @@ Pod::Spec.new do |s|
|
|||||||
cs.requires_arc = true
|
cs.requires_arc = true
|
||||||
cs.ios.library = 'z'
|
cs.ios.library = 'z'
|
||||||
cs.ios.frameworks = 'MobileCoreServices', 'CFNetwork'
|
cs.ios.frameworks = 'MobileCoreServices', 'CFNetwork'
|
||||||
|
cs.tvos.library = 'z'
|
||||||
|
cs.tvos.frameworks = 'MobileCoreServices', 'CFNetwork'
|
||||||
cs.osx.library = 'z'
|
cs.osx.library = 'z'
|
||||||
cs.osx.framework = 'SystemConfiguration'
|
cs.osx.framework = 'SystemConfiguration'
|
||||||
end
|
end
|
||||||
@@ -35,6 +38,7 @@ Pod::Spec.new do |s|
|
|||||||
cs.source_files = 'GCDWebDAVServer/*.{h,m}'
|
cs.source_files = 'GCDWebDAVServer/*.{h,m}'
|
||||||
cs.requires_arc = true
|
cs.requires_arc = true
|
||||||
cs.ios.library = 'xml2'
|
cs.ios.library = 'xml2'
|
||||||
|
cs.tvos.library = 'xml2'
|
||||||
cs.osx.library = 'xml2'
|
cs.osx.library = 'xml2'
|
||||||
cs.compiler_flags = '-I$(SDKROOT)/usr/include/libxml2'
|
cs.compiler_flags = '-I$(SDKROOT)/usr/include/libxml2'
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "0710"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "NO"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CEE28CD01AE004D800F4023C"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (Mac)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "E24039241BA09207000B7089"
|
||||||
|
BuildableName = "Tests.xctest"
|
||||||
|
BlueprintName = "Tests (Mac)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
enableAddressSanitizer = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CEE28CD01AE004D800F4023C"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (Mac)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
<AdditionalOption
|
||||||
|
key = "NSZombieEnabled"
|
||||||
|
value = "YES"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</AdditionalOption>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CEE28CD01AE004D800F4023C"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (Mac)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "0710"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "NO"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CEE28CEE1AE0051F00F4023C"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (iOS)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CEE28CEE1AE0051F00F4023C"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (iOS)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
enableAddressSanitizer = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CEE28CEE1AE0051F00F4023C"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (iOS)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
<AdditionalOption
|
||||||
|
key = "NSZombieEnabled"
|
||||||
|
value = "YES"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</AdditionalOption>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CEE28CEE1AE0051F00F4023C"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (iOS)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "0710"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "NO"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "E2DDD18A1BE69404002CE867"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (tvOS)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "E2DDD18A1BE69404002CE867"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (tvOS)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
enableAddressSanitizer = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "E2DDD18A1BE69404002CE867"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (tvOS)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
<AdditionalOption
|
||||||
|
key = "NSZombieEnabled"
|
||||||
|
value = "YES"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</AdditionalOption>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "E2DDD18A1BE69404002CE867"
|
||||||
|
BuildableName = "GCDWebServers.framework"
|
||||||
|
BlueprintName = "GCDWebServers (tvOS)"
|
||||||
|
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -90,14 +90,26 @@ extern NSString* const GCDWebServerOption_BonjourName;
|
|||||||
*/
|
*/
|
||||||
extern NSString* const GCDWebServerOption_BonjourType;
|
extern NSString* const GCDWebServerOption_BonjourType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a port mapping in the NAT gateway (NSNumber / BOOL).
|
||||||
|
*
|
||||||
|
* This uses the DNSService API under the hood which supports IPv4 mappings only.
|
||||||
|
*
|
||||||
|
* The default value is NO.
|
||||||
|
*
|
||||||
|
* @warning The external port set up by the NAT gateway may be different than
|
||||||
|
* the one used by the GCDWebServer.
|
||||||
|
*/
|
||||||
|
extern NSString* const GCDWebServerOption_RequestNATPortMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only accept HTTP requests coming from localhost i.e. not from the outside
|
* Only accept HTTP requests coming from localhost i.e. not from the outside
|
||||||
* network (NSNumber / BOOL).
|
* network (NSNumber / BOOL).
|
||||||
*
|
*
|
||||||
* The default value is NO.
|
* The default value is NO.
|
||||||
*
|
*
|
||||||
* @warning Bonjour should be disabled if using this option since the server
|
* @warning Bonjour and NAT port mapping should be disabled if using this option
|
||||||
* will not be reachable from the outside network anyway.
|
* since the server will not be reachable from the outside network anyway.
|
||||||
*/
|
*/
|
||||||
extern NSString* const GCDWebServerOption_BindToLocalhost;
|
extern NSString* const GCDWebServerOption_BindToLocalhost;
|
||||||
|
|
||||||
@@ -213,9 +225,21 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
/**
|
/**
|
||||||
* This method is called after the Bonjour registration for the server has
|
* This method is called after the Bonjour registration for the server has
|
||||||
* successfully completed.
|
* successfully completed.
|
||||||
|
*
|
||||||
|
* Use the "bonjourServerURL" property to retrieve the Bonjour address of the
|
||||||
|
* server.
|
||||||
*/
|
*/
|
||||||
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server;
|
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called after the NAT port mapping for the server has been
|
||||||
|
* updated.
|
||||||
|
*
|
||||||
|
* Use the "publicServerURL" property to retrieve the public address of the
|
||||||
|
* server.
|
||||||
|
*/
|
||||||
|
- (void)webServerDidUpdateNATPortMapping:(GCDWebServer*)server;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called when the first GCDWebServerConnection is opened by the
|
* This method is called when the first GCDWebServerConnection is opened by the
|
||||||
* server to serve a series of HTTP requests.
|
* server to serve a series of HTTP requests.
|
||||||
@@ -362,6 +386,14 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
*/
|
*/
|
||||||
@property(nonatomic, readonly) NSURL* bonjourServerURL;
|
@property(nonatomic, readonly) NSURL* bonjourServerURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server's public URL.
|
||||||
|
*
|
||||||
|
* @warning This property is only valid if the server is running and NAT port
|
||||||
|
* mapping is active.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) NSURL* publicServerURL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
||||||
* using the default Bonjour name.
|
* using the default Bonjour name.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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,6 +38,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#import <netinet/in.h>
|
#import <netinet/in.h>
|
||||||
|
#import <dns_sd.h>
|
||||||
|
|
||||||
#import "GCDWebServerPrivate.h"
|
#import "GCDWebServerPrivate.h"
|
||||||
|
|
||||||
@@ -47,9 +48,12 @@
|
|||||||
#define kDefaultPort 8080
|
#define kDefaultPort 8080
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define kBonjourResolutionTimeout 5.0
|
||||||
|
|
||||||
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_RequestNATPortMapping = @"RequestNATPortMapping";
|
||||||
NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost";
|
NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost";
|
||||||
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
|
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
|
||||||
NSString* const GCDWebServerOption_ServerName = @"ServerName";
|
NSString* const GCDWebServerOption_ServerName = @"ServerName";
|
||||||
@@ -74,9 +78,9 @@ GCDWebServerLoggingLevel GCDWebServerLogLevel = kGCDWebServerLoggingLevel_Info;
|
|||||||
#endif
|
#endif
|
||||||
#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__)
|
#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__)
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
int GCDWebServerLogLevel = LOG_LEVEL_DEBUG;
|
DDLogLevel GCDWebServerLogLevel = DDLogLevelDebug;
|
||||||
#else
|
#else
|
||||||
int GCDWebServerLogLevel = LOG_LEVEL_INFO;
|
DDLogLevel GCDWebServerLogLevel = DDLogLevelInfo;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -171,6 +175,12 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
dispatch_source_t _source6;
|
dispatch_source_t _source6;
|
||||||
CFNetServiceRef _registrationService;
|
CFNetServiceRef _registrationService;
|
||||||
CFNetServiceRef _resolutionService;
|
CFNetServiceRef _resolutionService;
|
||||||
|
DNSServiceRef _dnsService;
|
||||||
|
CFSocketRef _dnsSocket;
|
||||||
|
CFRunLoopSourceRef _dnsSource;
|
||||||
|
NSString* _dnsAddress;
|
||||||
|
NSUInteger _dnsPort;
|
||||||
|
BOOL _bindToLocalhost;
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
BOOL _suspendInBackground;
|
BOOL _suspendInBackground;
|
||||||
UIBackgroundTaskIdentifier _backgroundTask;
|
UIBackgroundTaskIdentifier _backgroundTask;
|
||||||
@@ -243,7 +253,9 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
GWS_LOG_DEBUG(@"Did connect");
|
GWS_LOG_DEBUG(@"Did connect");
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
[self _startBackgroundTask];
|
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
||||||
|
[self _startBackgroundTask];
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
|
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
|
||||||
@@ -284,8 +296,6 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
|
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
|
||||||
_backgroundTask = UIBackgroundTaskInvalid;
|
_backgroundTask = UIBackgroundTaskInvalid;
|
||||||
GWS_LOG_DEBUG(@"Did end background task");
|
GWS_LOG_DEBUG(@"Did end background task");
|
||||||
} else if ([UIApplication sharedApplication]) {
|
|
||||||
GWS_DNOT_REACHED();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,7 +378,10 @@ static void _NetServiceRegisterCallBack(CFNetServiceRef service, CFStreamError*
|
|||||||
} else {
|
} else {
|
||||||
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
||||||
GWS_LOG_VERBOSE(@"Bonjour registration complete for %@", [server class]);
|
GWS_LOG_VERBOSE(@"Bonjour registration complete for %@", [server class]);
|
||||||
CFNetServiceResolveWithTimeout(server->_resolutionService, 1.0, NULL);
|
if (!CFNetServiceResolveWithTimeout(server->_resolutionService, kBonjourResolutionTimeout, NULL)) {
|
||||||
|
GWS_LOG_ERROR(@"Failed starting Bonjour resolution");
|
||||||
|
GWS_DNOT_REACHED();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,7 +395,7 @@ static void _NetServiceResolveCallBack(CFNetServiceRef service, CFStreamError* e
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
||||||
GWS_LOG_INFO(@"%@ now reachable at %@", [server class], server.bonjourServerURL);
|
GWS_LOG_INFO(@"%@ now locally reachable at %@", [server class], server.bonjourServerURL);
|
||||||
if ([server.delegate respondsToSelector:@selector(webServerDidCompleteBonjourRegistration:)]) {
|
if ([server.delegate respondsToSelector:@selector(webServerDidCompleteBonjourRegistration:)]) {
|
||||||
[server.delegate webServerDidCompleteBonjourRegistration:server];
|
[server.delegate webServerDidCompleteBonjourRegistration:server];
|
||||||
}
|
}
|
||||||
@@ -390,6 +403,41 @@ static void _NetServiceResolveCallBack(CFNetServiceRef service, CFStreamError* e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _DNSServiceCallBack(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, uint32_t externalAddress, DNSServiceProtocol protocol, uint16_t internalPort, uint16_t externalPort, uint32_t ttl, void* context) {
|
||||||
|
GWS_DCHECK([NSThread isMainThread]);
|
||||||
|
@autoreleasepool {
|
||||||
|
GCDWebServer* server = (__bridge GCDWebServer*)context;
|
||||||
|
if ((errorCode == kDNSServiceErr_NoError) || (errorCode == kDNSServiceErr_DoubleNAT)) {
|
||||||
|
struct sockaddr_in addr4;
|
||||||
|
bzero(&addr4, sizeof(addr4));
|
||||||
|
addr4.sin_len = sizeof(addr4);
|
||||||
|
addr4.sin_family = AF_INET;
|
||||||
|
addr4.sin_addr.s_addr = externalAddress; // Already in network byte order
|
||||||
|
server->_dnsAddress = GCDWebServerStringFromSockAddr((const struct sockaddr*)&addr4, NO);
|
||||||
|
server->_dnsPort = ntohs(externalPort);
|
||||||
|
GWS_LOG_INFO(@"%@ now publicly reachable at %@", [server class], server.publicServerURL);
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"DNS service error %i", errorCode);
|
||||||
|
server->_dnsAddress = nil;
|
||||||
|
server->_dnsPort = 0;
|
||||||
|
}
|
||||||
|
if ([server.delegate respondsToSelector:@selector(webServerDidUpdateNATPortMapping:)]) {
|
||||||
|
[server.delegate webServerDidUpdateNATPortMapping:server];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
|
||||||
|
GWS_DCHECK([NSThread isMainThread]);
|
||||||
|
@autoreleasepool {
|
||||||
|
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
||||||
|
DNSServiceErrorType status = DNSServiceProcessResult(server->_dnsService);
|
||||||
|
if (status != kDNSServiceErr_NoError) {
|
||||||
|
GWS_LOG_ERROR(@"DNS service error %i", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValue) {
|
static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValue) {
|
||||||
id value = [options objectForKey:key];
|
id value = [options objectForKey:key];
|
||||||
return value ? value : defaultValue;
|
return value ? value : defaultValue;
|
||||||
@@ -462,18 +510,18 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
dispatch_source_set_event_handler(source, ^{
|
dispatch_source_set_event_handler(source, ^{
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
struct sockaddr remoteSockAddr;
|
struct sockaddr_storage remoteSockAddr;
|
||||||
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
|
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
|
||||||
int socket = accept(listeningSocket, &remoteSockAddr, &remoteAddrLen);
|
int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen);
|
||||||
if (socket > 0) {
|
if (socket > 0) {
|
||||||
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
|
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
|
||||||
|
|
||||||
struct sockaddr localSockAddr;
|
struct sockaddr_storage localSockAddr;
|
||||||
socklen_t localAddrLen = sizeof(localSockAddr);
|
socklen_t localAddrLen = sizeof(localSockAddr);
|
||||||
NSData* localAddress = nil;
|
NSData* localAddress = nil;
|
||||||
if (getsockname(socket, &localSockAddr, &localAddrLen) == 0) {
|
if (getsockname(socket, (struct sockaddr*)&localSockAddr, &localAddrLen) == 0) {
|
||||||
localAddress = [NSData dataWithBytes:&localSockAddr length:localAddrLen];
|
localAddress = [NSData dataWithBytes:&localSockAddr length:localAddrLen];
|
||||||
GWS_DCHECK((!isIPv6 && localSockAddr.sa_family == AF_INET) || (isIPv6 && localSockAddr.sa_family == AF_INET6));
|
GWS_DCHECK((!isIPv6 && localSockAddr.ss_family == AF_INET) || (isIPv6 && localSockAddr.ss_family == AF_INET6));
|
||||||
} else {
|
} else {
|
||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -510,11 +558,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
struct sockaddr addr;
|
struct sockaddr_in addr;
|
||||||
socklen_t addrlen = sizeof(addr);
|
socklen_t addrlen = sizeof(addr);
|
||||||
if (getsockname(listeningSocket4, &addr, &addrlen) == 0) {
|
if (getsockname(listeningSocket4, (struct sockaddr*)&addr, &addrlen) == 0) {
|
||||||
struct sockaddr_in* sockaddr = (struct sockaddr_in*)&addr;
|
port = ntohs(addr.sin_port);
|
||||||
port = ntohs(sockaddr->sin_port);
|
|
||||||
} else {
|
} else {
|
||||||
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
|
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
|
||||||
}
|
}
|
||||||
@@ -556,6 +603,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
|
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
|
||||||
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
|
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
|
||||||
_port = port;
|
_port = port;
|
||||||
|
_bindToLocalhost = bindToLocalhost;
|
||||||
|
|
||||||
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil);
|
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil);
|
||||||
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
||||||
@@ -573,9 +621,34 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
if (_resolutionService) {
|
if (_resolutionService) {
|
||||||
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
||||||
CFNetServiceScheduleWithRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
CFNetServiceScheduleWithRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed creating CFNetService for resolution");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GWS_LOG_ERROR(@"Failed creating CFNetService");
|
GWS_LOG_ERROR(@"Failed creating CFNetService for registration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
|
||||||
|
DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self);
|
||||||
|
if (status == kDNSServiceErr_NoError) {
|
||||||
|
CFSocketContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
||||||
|
_dnsSocket = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(_dnsService), kCFSocketReadCallBack, _SocketCallBack, &context);
|
||||||
|
if (_dnsSocket) {
|
||||||
|
CFSocketSetSocketFlags(_dnsSocket, CFSocketGetSocketFlags(_dnsSocket) & ~kCFSocketCloseOnInvalidate);
|
||||||
|
_dnsSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _dnsSocket, 0);
|
||||||
|
if (_dnsSource) {
|
||||||
|
CFRunLoopAddSource(CFRunLoopGetMain(), _dnsSource, kCFRunLoopCommonModes);
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed creating CFRunLoopSource");
|
||||||
|
GWS_DNOT_REACHED();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed creating CFSocket");
|
||||||
|
GWS_DNOT_REACHED();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,6 +667,22 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
- (void)_stop {
|
- (void)_stop {
|
||||||
GWS_DCHECK(_source4 != NULL);
|
GWS_DCHECK(_source4 != NULL);
|
||||||
|
|
||||||
|
if (_dnsService) {
|
||||||
|
_dnsAddress = nil;
|
||||||
|
_dnsPort = 0;
|
||||||
|
if (_dnsSource) {
|
||||||
|
CFRunLoopSourceInvalidate(_dnsSource);
|
||||||
|
CFRelease(_dnsSource);
|
||||||
|
_dnsSource = NULL;
|
||||||
|
}
|
||||||
|
if (_dnsSocket) {
|
||||||
|
CFRelease(_dnsSocket);
|
||||||
|
_dnsSocket = NULL;
|
||||||
|
}
|
||||||
|
DNSServiceRefDeallocate(_dnsService);
|
||||||
|
_dnsService = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (_registrationService) {
|
if (_registrationService) {
|
||||||
if (_resolutionService) {
|
if (_resolutionService) {
|
||||||
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
@@ -621,6 +710,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
#endif
|
#endif
|
||||||
_source4 = NULL;
|
_source4 = NULL;
|
||||||
_port = 0;
|
_port = 0;
|
||||||
|
_bindToLocalhost = NO;
|
||||||
|
|
||||||
_serverName = nil;
|
_serverName = nil;
|
||||||
_authenticationRealm = nil;
|
_authenticationRealm = nil;
|
||||||
@@ -666,7 +756,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
|
|
||||||
- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error {
|
- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error {
|
||||||
if (_options == nil) {
|
if (_options == nil) {
|
||||||
_options = [options copy];
|
_options = options ? [options copy] : @{};
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
_suspendInBackground = [_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue];
|
_suspendInBackground = [_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])
|
||||||
@@ -717,7 +807,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
|
|
||||||
- (NSURL*)serverURL {
|
- (NSURL*)serverURL {
|
||||||
if (_source4) {
|
if (_source4) {
|
||||||
NSString* ipAddress = GCDWebServerGetPrimaryIPAddress(NO); // We can't really use IPv6 anyway as it doesn't work great with HTTP URLs in practice
|
NSString* ipAddress = _bindToLocalhost ? @"localhost" : GCDWebServerGetPrimaryIPAddress(NO); // We can't really use IPv6 anyway as it doesn't work great with HTTP URLs in practice
|
||||||
if (ipAddress) {
|
if (ipAddress) {
|
||||||
if (_port != 80) {
|
if (_port != 80) {
|
||||||
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", ipAddress, (int)_port]];
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", ipAddress, (int)_port]];
|
||||||
@@ -744,6 +834,17 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSURL*)publicServerURL {
|
||||||
|
if (_source4 && _dnsService && _dnsAddress && _dnsPort) {
|
||||||
|
if (_dnsPort != 80) {
|
||||||
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", _dnsAddress, (int)_dnsPort]];
|
||||||
|
} else {
|
||||||
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", _dnsAddress]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)start {
|
- (BOOL)start {
|
||||||
return [self startWithPort:kDefaultPort bonjourName:@""];
|
return [self startWithPort:kDefaultPort bonjourName:@""];
|
||||||
}
|
}
|
||||||
@@ -912,7 +1013,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
for (NSString* file in enumerator) {
|
for (NSString* file in enumerator) {
|
||||||
if (![file hasPrefix:@"."]) {
|
if (![file hasPrefix:@"."]) {
|
||||||
NSString* type = [[enumerator fileAttributes] objectForKey:NSFileType];
|
NSString* type = [[enumerator fileAttributes] objectForKey:NSFileType];
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
NSString* escapedFile = [file stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
NSString* escapedFile = [file stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
#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, file];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -548,6 +548,8 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_request) {
|
if (_request) {
|
||||||
|
_request.localAddressData = self.localAddressData;
|
||||||
|
_request.remoteAddressData = self.remoteAddressData;
|
||||||
if ([_request hasBody]) {
|
if ([_request hasBody]) {
|
||||||
[_request prepareForWriting];
|
[_request prepareForWriting];
|
||||||
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
|
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
|
||||||
@@ -757,22 +759,26 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion {
|
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion {
|
||||||
GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
||||||
@try {
|
@try {
|
||||||
_handler.asyncProcessBlock(request, completion);
|
_handler.asyncProcessBlock(request, [completion copy]);
|
||||||
}
|
}
|
||||||
@catch (NSException* exception) {
|
@catch (NSException* exception) {
|
||||||
GWS_LOG_EXCEPTION(exception);
|
GWS_LOG_EXCEPTION(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
|
||||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
|
||||||
static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) {
|
static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) {
|
||||||
if ([requestETag isEqualToString:@"*"] && (!responseLastModified || !requestLastModified || ([responseLastModified compare:requestLastModified] != NSOrderedDescending))) {
|
if (requestLastModified && responseLastModified) {
|
||||||
return YES;
|
if ([responseLastModified compare:requestLastModified] != NSOrderedDescending) {
|
||||||
} else {
|
|
||||||
if ([responseETag isEqualToString:requestETag]) {
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
if (responseLastModified && requestLastModified && ([responseLastModified compare:requestLastModified] != NSOrderedDescending)) {
|
}
|
||||||
|
if (requestETag && responseETag) { // Per the specs "If-None-Match" must be checked after "If-Modified-Since"
|
||||||
|
if ([requestETag isEqualToString:@"*"]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
if ([responseETag isEqualToString:requestETag]) {
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -182,11 +182,17 @@ NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSString* GCDWebServerEscapeURLString(NSString* string) {
|
NSString* GCDWebServerEscapeURLString(NSString* string) {
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8));
|
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8));
|
||||||
|
#pragma clang diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* GCDWebServerUnescapeURLString(NSString* string) {
|
NSString* GCDWebServerUnescapeURLString(NSString* string) {
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8));
|
return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8));
|
||||||
|
#pragma clang diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -87,15 +87,15 @@
|
|||||||
* it as a logging facility.
|
* it as a logging facility.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#elif defined(__has_include) && __has_include("DDLogMacros.h")
|
#elif defined(__has_include) && __has_include("CocoaLumberjack/CocoaLumberjack.h")
|
||||||
|
|
||||||
#import "DDLogMacros.h"
|
#import <CocoaLumberjack/CocoaLumberjack.h>
|
||||||
|
|
||||||
#define __GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__
|
#define __GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__
|
||||||
|
|
||||||
#undef LOG_LEVEL_DEF
|
#undef LOG_LEVEL_DEF
|
||||||
#define LOG_LEVEL_DEF GCDWebServerLogLevel
|
#define LOG_LEVEL_DEF GCDWebServerLogLevel
|
||||||
extern int GCDWebServerLogLevel;
|
extern DDLogLevel GCDWebServerLogLevel;
|
||||||
|
|
||||||
#define GWS_LOG_DEBUG(...) DDLogDebug(__VA_ARGS__)
|
#define GWS_LOG_DEBUG(...) DDLogDebug(__VA_ARGS__)
|
||||||
#define GWS_LOG_VERBOSE(...) DDLogVerbose(__VA_ARGS__)
|
#define GWS_LOG_VERBOSE(...) DDLogVerbose(__VA_ARGS__)
|
||||||
@@ -211,6 +211,8 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
|
|||||||
|
|
||||||
@interface GCDWebServerRequest ()
|
@interface GCDWebServerRequest ()
|
||||||
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
|
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
|
||||||
|
@property(nonatomic, readwrite) NSData* localAddressData;
|
||||||
|
@property(nonatomic, readwrite) NSData* remoteAddressData;
|
||||||
- (void)prepareForWriting;
|
- (void)prepareForWriting;
|
||||||
- (BOOL)performOpen:(NSError**)error;
|
- (BOOL)performOpen:(NSError**)error;
|
||||||
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error;
|
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -157,6 +157,30 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
|
|||||||
*/
|
*/
|
||||||
@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding;
|
@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the address of the local peer (i.e. server) for the request
|
||||||
|
* as a raw "struct sockaddr".
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) NSData* localAddressData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the address of the local peer (i.e. server) for the request
|
||||||
|
* as a string.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) NSString* localAddressString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the address of the remote peer (i.e. client) for the request
|
||||||
|
* as a raw "struct sockaddr".
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) NSData* remoteAddressData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the address of the remote peer (i.e. client) for the request
|
||||||
|
* as a string.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) NSString* remoteAddressString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is the designated initializer for the class.
|
* This method is the designated initializer for the class.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -88,7 +88,9 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
- (BOOL)open:(NSError**)error {
|
- (BOOL)open:(NSError**)error {
|
||||||
int result = inflateInit2(&_stream, 15 + 16);
|
int result = inflateInit2(&_stream, 15 + 16);
|
||||||
if (result != Z_OK) {
|
if (result != Z_OK) {
|
||||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
if (error) {
|
||||||
|
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
if (![super open:error]) {
|
if (![super open:error]) {
|
||||||
@@ -114,7 +116,9 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
_stream.avail_out = (uInt)maxLength;
|
_stream.avail_out = (uInt)maxLength;
|
||||||
int result = inflate(&_stream, Z_NO_FLUSH);
|
int result = inflate(&_stream, Z_NO_FLUSH);
|
||||||
if ((result != Z_OK) && (result != Z_STREAM_END)) {
|
if ((result != Z_OK) && (result != Z_STREAM_END)) {
|
||||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
if (error) {
|
||||||
|
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
length += maxLength - _stream.avail_out;
|
length += maxLength - _stream.avail_out;
|
||||||
@@ -153,6 +157,8 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
NSString* _noneMatch;
|
NSString* _noneMatch;
|
||||||
NSRange _range;
|
NSRange _range;
|
||||||
BOOL _gzipAccepted;
|
BOOL _gzipAccepted;
|
||||||
|
NSData* _localAddress;
|
||||||
|
NSData* _remoteAddress;
|
||||||
|
|
||||||
BOOL _opened;
|
BOOL _opened;
|
||||||
NSMutableArray* _decoders;
|
NSMutableArray* _decoders;
|
||||||
@@ -164,7 +170,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
@implementation GCDWebServerRequest : NSObject
|
@implementation GCDWebServerRequest : NSObject
|
||||||
|
|
||||||
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, ifModifiedSince=_modifiedSince, ifNoneMatch=_noneMatch,
|
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, ifModifiedSince=_modifiedSince, ifNoneMatch=_noneMatch,
|
||||||
byteRange=_range, acceptsGzipContentEncoding=_gzipAccepted, usesChunkedTransferEncoding=_chunked;
|
byteRange=_range, acceptsGzipContentEncoding=_gzipAccepted, usesChunkedTransferEncoding=_chunked, localAddressData=_localAddress, remoteAddressData=_remoteAddress;
|
||||||
|
|
||||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
@@ -180,6 +186,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
if (lengthHeader) {
|
if (lengthHeader) {
|
||||||
NSInteger length = [lengthHeader integerValue];
|
NSInteger length = [lengthHeader integerValue];
|
||||||
if (_chunked || (length < 0)) {
|
if (_chunked || (length < 0)) {
|
||||||
|
GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _url);
|
||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@@ -194,8 +201,8 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
_length = NSUIntegerMax;
|
_length = NSUIntegerMax;
|
||||||
} else {
|
} else {
|
||||||
if (_type) {
|
if (_type) {
|
||||||
GWS_DNOT_REACHED();
|
GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _url);
|
||||||
return nil;
|
_type = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense
|
||||||
}
|
}
|
||||||
_length = NSUIntegerMax;
|
_length = NSUIntegerMax;
|
||||||
}
|
}
|
||||||
@@ -304,6 +311,14 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
[_attributes setValue:attribute forKey:key];
|
[_attributes setValue:attribute forKey:key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString*)localAddressString {
|
||||||
|
return GCDWebServerStringFromSockAddr(_localAddress.bytes, YES);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)remoteAddressString {
|
||||||
|
return GCDWebServerStringFromSockAddr(_remoteAddress.bytes, YES);
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString*)description {
|
- (NSString*)description {
|
||||||
NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path];
|
NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path];
|
||||||
for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -94,7 +94,9 @@
|
|||||||
- (BOOL)open:(NSError**)error {
|
- (BOOL)open:(NSError**)error {
|
||||||
int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||||
if (result != Z_OK) {
|
if (result != Z_OK) {
|
||||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
if (error) {
|
||||||
|
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
if (![super open:error]) {
|
if (![super open:error]) {
|
||||||
@@ -130,7 +132,9 @@
|
|||||||
if (result == Z_STREAM_END) {
|
if (result == Z_STREAM_END) {
|
||||||
_finished = YES;
|
_finished = YES;
|
||||||
} else if (result != Z_OK) {
|
} else if (result != Z_OK) {
|
||||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
if (error) {
|
||||||
|
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||||
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
length += maxLength - _stream.avail_out;
|
length += maxLength - _stream.avail_out;
|
||||||
@@ -238,7 +242,7 @@
|
|||||||
|
|
||||||
- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
|
- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
|
||||||
if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) {
|
if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) {
|
||||||
[_reader asyncReadDataWithCompletion:block];
|
[_reader asyncReadDataWithCompletion:[block copy]];
|
||||||
} else {
|
} else {
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
NSData* data = [_reader readData:&error];
|
NSData* data = [_reader readData:&error];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -51,7 +51,9 @@
|
|||||||
_data = [[NSMutableData alloc] init];
|
_data = [[NSMutableData alloc] init];
|
||||||
}
|
}
|
||||||
if (_data == nil) {
|
if (_data == nil) {
|
||||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed allocating memory"}];
|
if (error) {
|
||||||
|
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed allocating memory"}];
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -56,7 +56,9 @@
|
|||||||
- (BOOL)open:(NSError**)error {
|
- (BOOL)open:(NSError**)error {
|
||||||
_file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
_file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||||
if (_file <= 0) {
|
if (_file <= 0) {
|
||||||
*error = GCDWebServerMakePosixError(errno);
|
if (error) {
|
||||||
|
*error = GCDWebServerMakePosixError(errno);
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
@@ -64,7 +66,9 @@
|
|||||||
|
|
||||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||||
if (write(_file, data.bytes, data.length) != (ssize_t)data.length) {
|
if (write(_file, data.bytes, data.length) != (ssize_t)data.length) {
|
||||||
*error = GCDWebServerMakePosixError(errno);
|
if (error) {
|
||||||
|
*error = GCDWebServerMakePosixError(errno);
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
@@ -72,7 +76,9 @@
|
|||||||
|
|
||||||
- (BOOL)close:(NSError**)error {
|
- (BOOL)close:(NSError**)error {
|
||||||
if (close(_file) < 0) {
|
if (close(_file) < 0) {
|
||||||
*error = GCDWebServerMakePosixError(errno);
|
if (error) {
|
||||||
|
*error = GCDWebServerMakePosixError(errno);
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -376,7 +376,9 @@ static NSData* _dashNewlineData = nil;
|
|||||||
NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary");
|
NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary");
|
||||||
_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) {
|
||||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed starting to parse multipart form data"}];
|
if (error) {
|
||||||
|
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed starting to parse multipart form data"}];
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
@@ -384,7 +386,9 @@ 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]) {
|
||||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed continuing to parse multipart form data"}];
|
if (error) {
|
||||||
|
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed continuing to parse multipart form data"}];
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
@@ -394,7 +398,9 @@ static NSData* _dashNewlineData = nil;
|
|||||||
BOOL atEnd = [_parser isAtEnd];
|
BOOL atEnd = [_parser isAtEnd];
|
||||||
_parser = nil;
|
_parser = nil;
|
||||||
if (!atEnd) {
|
if (!atEnd) {
|
||||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed finishing to parse multipart form data"}];
|
if (error) {
|
||||||
|
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed finishing to parse multipart form data"}];
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -142,11 +142,15 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
|||||||
- (BOOL)open:(NSError**)error {
|
- (BOOL)open:(NSError**)error {
|
||||||
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
||||||
if (_file <= 0) {
|
if (_file <= 0) {
|
||||||
*error = GCDWebServerMakePosixError(errno);
|
if (error) {
|
||||||
|
*error = GCDWebServerMakePosixError(errno);
|
||||||
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
|
if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
|
||||||
*error = GCDWebServerMakePosixError(errno);
|
if (error) {
|
||||||
|
*error = GCDWebServerMakePosixError(errno);
|
||||||
|
}
|
||||||
close(_file);
|
close(_file);
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
@@ -158,7 +162,9 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
|||||||
NSMutableData* data = [[NSMutableData alloc] initWithLength:length];
|
NSMutableData* data = [[NSMutableData alloc] initWithLength:length];
|
||||||
ssize_t result = read(_file, data.mutableBytes, length);
|
ssize_t result = read(_file, data.mutableBytes, length);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
*error = GCDWebServerMakePosixError(errno);
|
if (error) {
|
||||||
|
*error = GCDWebServerMakePosixError(errno);
|
||||||
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The GCDWebServerStreamBlock is called to stream the data for the HTTP body.
|
* The GCDWebServerStreamBlock is called to stream the data for the HTTP body.
|
||||||
* The block must return empty NSData when done or nil on error and set the
|
* The block must return either a chunk of data, an empty NSData when done, or
|
||||||
* "error" argument which is guaranteed to be non-NULL.
|
* nil on error and set the "error" argument which is guaranteed to be non-NULL.
|
||||||
*/
|
*/
|
||||||
typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
|
typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
|
||||||
|
|
||||||
@@ -39,13 +39,10 @@ typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
|
|||||||
* except the streamed data can be returned at a later time allowing for
|
* except the streamed data can be returned at a later time allowing for
|
||||||
* truly asynchronous generation of the data.
|
* truly asynchronous generation of the data.
|
||||||
*
|
*
|
||||||
* The block must return empty NSData when done or nil on error and set the
|
* The block must call "completionBlock" passing the new chunk of data when ready,
|
||||||
* "error" argument which is guaranteed to be non-NULL.
|
* an empty NSData when done, or nil on error and pass a NSError.
|
||||||
*
|
*
|
||||||
* The block must regularly call "completionBlock" passing new streamed data.
|
* The block cannot call "completionBlock" more than once per invocation.
|
||||||
* Eventually it must call "completionBlock" passing an empty NSData indicating
|
|
||||||
* the end of the stream has been reached, or pass nil and an NSError in case of
|
|
||||||
* error.
|
|
||||||
*/
|
*/
|
||||||
typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock);
|
typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -292,9 +292,11 @@
|
|||||||
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden,
|
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden,
|
||||||
title=_title, header=_header, prologue=_prologue, epilogue=_epilogue, footer=_footer;
|
title=_title, header=_header, prologue=_prologue, epilogue=_epilogue, footer=_footer;
|
||||||
|
|
||||||
|
@dynamic delegate;
|
||||||
|
|
||||||
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
NSBundle* siteBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"GCDWebUploader" ofType:@"bundle"]];
|
NSBundle* siteBundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[GCDWebUploader class]] pathForResource:@"GCDWebUploader" ofType:@"bundle"]];
|
||||||
if (siteBundle == nil) {
|
if (siteBundle == nil) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|||||||
35
Mac/main.m
35
Mac/main.m
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -72,6 +72,10 @@ typedef enum {
|
|||||||
[self _logDelegateCall:_cmd];
|
[self _logDelegateCall:_cmd];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)webServerDidUpdateNATPortMapping:(GCDWebServer*)server {
|
||||||
|
[self _logDelegateCall:_cmd];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)webServerDidConnect:(GCDWebServer*)server {
|
- (void)webServerDidConnect:(GCDWebServer*)server {
|
||||||
[self _logDelegateCall:_cmd];
|
[self _logDelegateCall:_cmd];
|
||||||
}
|
}
|
||||||
@@ -142,6 +146,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
NSString* authenticationUser = nil;
|
NSString* authenticationUser = nil;
|
||||||
NSString* authenticationPassword = nil;
|
NSString* authenticationPassword = nil;
|
||||||
BOOL bindToLocalhost = NO;
|
BOOL bindToLocalhost = NO;
|
||||||
|
BOOL requestNATPortMapping = NO;
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse | asyncResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password] [--localhost]\n\n", basename((char*)argv[0]));
|
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse | asyncResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password] [--localhost]\n\n", basename((char*)argv[0]));
|
||||||
@@ -191,6 +196,8 @@ int main(int argc, const char* argv[]) {
|
|||||||
authenticationPassword = [NSString stringWithUTF8String:argv[i]];
|
authenticationPassword = [NSString stringWithUTF8String:argv[i]];
|
||||||
} else if (!strcmp(argv[i], "--localhost")) {
|
} else if (!strcmp(argv[i], "--localhost")) {
|
||||||
bindToLocalhost = YES;
|
bindToLocalhost = YES;
|
||||||
|
} else if (!strcmp(argv[i], "--nat")) {
|
||||||
|
requestNATPortMapping = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,7 +364,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
fprintf(stdout, "Running in Async Response mode");
|
fprintf(stdout, "Running in Async Response mode");
|
||||||
webServer = [[GCDWebServer alloc] init];
|
webServer = [[GCDWebServer alloc] init];
|
||||||
[webServer addHandlerForMethod:@"GET"
|
[webServer addHandlerForMethod:@"GET"
|
||||||
path:@"/"
|
path:@"/async"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
|
|
||||||
@@ -366,6 +373,29 @@ int main(int argc, const char* argv[]) {
|
|||||||
completionBlock(response);
|
completionBlock(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
}];
|
||||||
|
[webServer addHandlerForMethod:@"GET"
|
||||||
|
path:@"/async2"
|
||||||
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
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), ^{
|
||||||
|
|
||||||
|
__block int countDown = 10;
|
||||||
|
GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock readerCompletionBlock) {
|
||||||
|
|
||||||
|
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];
|
||||||
|
readerCompletionBlock(data, nil);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}];
|
||||||
|
handlerCompletionBlock(response);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -389,6 +419,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
NSMutableDictionary* options = [NSMutableDictionary dictionary];
|
NSMutableDictionary* options = [NSMutableDictionary dictionary];
|
||||||
[options setObject:@8080 forKey:GCDWebServerOption_Port];
|
[options setObject:@8080 forKey:GCDWebServerOption_Port];
|
||||||
|
[options setObject:@(requestNATPortMapping) forKey:GCDWebServerOption_RequestNATPortMapping];
|
||||||
[options setObject:@(bindToLocalhost) forKey:GCDWebServerOption_BindToLocalhost];
|
[options setObject:@(bindToLocalhost) forKey:GCDWebServerOption_BindToLocalhost];
|
||||||
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
||||||
if (authenticationUser && authenticationPassword) {
|
if (authenticationUser && authenticationPassword) {
|
||||||
|
|||||||
164
README.md
164
README.md
@@ -6,8 +6,6 @@ Overview
|
|||||||
[](https://github.com/swisspol/GCDWebServer)
|
[](https://github.com/swisspol/GCDWebServer)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
*ANNOUNCEMENT: If you like GCDWebServer, check out [XLFacility](https://github.com/swisspol/XLFacility), an elegant and powerful logging facility for OS X & iOS by the same author and also open-source. XLFacility can be used seemlessly to handle logging from GCDWebServer (see "Logging in GCDWebServer" below).*
|
|
||||||
|
|
||||||
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 OS X & iOS apps. It was written from scratch with the following goals in mind:
|
||||||
* Elegant and easy to use architecture with only 4 core classes: server, connection, request and response (see "Understanding GCDWebServer's Architecture" below)
|
* 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
|
||||||
@@ -26,6 +24,7 @@ Extra built-in features:
|
|||||||
* [Basic](https://en.wikipedia.org/wiki/Basic_access_authentication) and [Digest Access](https://en.wikipedia.org/wiki/Digest_access_authentication) authentications for password protection
|
* [Basic](https://en.wikipedia.org/wiki/Basic_access_authentication) and [Digest Access](https://en.wikipedia.org/wiki/Digest_access_authentication) authentications for password protection
|
||||||
* Automatically handle transitions between foreground, background and suspended modes in iOS apps
|
* Automatically handle transitions between foreground, background and suspended modes in iOS apps
|
||||||
* Full support for both IPv4 and IPv6
|
* Full support for both IPv4 and IPv6
|
||||||
|
* NAT port mapping (IPv4 only)
|
||||||
|
|
||||||
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
|
||||||
@@ -45,7 +44,7 @@ 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.
|
||||||
|
|
||||||
Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Xcode project's Podfile:
|
Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Podfile:
|
||||||
```
|
```
|
||||||
pod "GCDWebServer", "~> 3.0"
|
pod "GCDWebServer", "~> 3.0"
|
||||||
```
|
```
|
||||||
@@ -58,11 +57,29 @@ Or this line for GCDWebDAVServer:
|
|||||||
pod "GCDWebServer/WebDAV", "~> 3.0"
|
pod "GCDWebServer/WebDAV", "~> 3.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
And finally run `$ pod install`.
|
||||||
|
|
||||||
|
You can also use [Carthage](https://github.com/Carthage/Carthage) by adding this line to your Cartfile (3.2.5 is the first release with Carthage support):
|
||||||
|
```
|
||||||
|
github "swisspol/GCDWebServer" ~> 3.2.5
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run `$ carthage update` and add the generated frameworks to your Xcode projects (see [Carthage instructions](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application)).
|
||||||
|
|
||||||
|
Help & Support
|
||||||
|
==============
|
||||||
|
|
||||||
|
For help with using GCDWebServer, it's best to ask your question on Stack Overflow with the [`gcdwebserver`](http://stackoverflow.com/questions/tagged/gcdwebserver) tag. Be sure to read this entire README first though!
|
||||||
|
|
||||||
|
For bug reports or enhancement requests, please use [GitHub issues](https://github.com/swisspol/GCDWebServer/issues) instead.
|
||||||
|
|
||||||
Hello World
|
Hello World
|
||||||
===========
|
===========
|
||||||
|
|
||||||
These code snippets show how to implement a custom HTTP server that runs on port 8080 and returns a "Hello World" HTML page to any request. Since GCDWebServer uses GCD blocks to handle requests, no subclassing or delegates are needed, which results in very clean code.
|
These code snippets show how to implement a custom HTTP server that runs on port 8080 and returns a "Hello World" HTML page to any request. Since GCDWebServer uses GCD blocks to handle requests, no subclassing or delegates are needed, which results in very clean code.
|
||||||
|
|
||||||
|
**IMPORTANT:** If not using CocoaPods, be sure to add the `libz` shared system library to the Xcode target for your app.
|
||||||
|
|
||||||
**OS X version (command line tool):**
|
**OS X version (command line tool):**
|
||||||
```objectivec
|
```objectivec
|
||||||
#import "GCDWebServer.h"
|
#import "GCDWebServer.h"
|
||||||
@@ -134,83 +151,29 @@ int main(int argc, const char* argv[]) {
|
|||||||
***webServer.swift***
|
***webServer.swift***
|
||||||
```swift
|
```swift
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import GCDWebServers
|
||||||
|
|
||||||
let webServer = GCDWebServer()
|
func initWebServer() {
|
||||||
|
|
||||||
webServer.addDefaultHandlerForMethod("GET", requestClass: GCDWebServerRequest.self) { request in
|
let webServer = GCDWebServer()
|
||||||
|
|
||||||
|
webServer.addDefaultHandlerForMethod("GET", requestClass: 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.runWithPort(8080, bonjourName: "GCD Web Server")
|
||||||
|
|
||||||
|
print("Visit \(webServer.serverURL) in your web browser")
|
||||||
}
|
}
|
||||||
|
|
||||||
webServer.runWithPort(8080, bonjourName: nil)
|
|
||||||
|
|
||||||
println("Visit \(webServer.serverURL) in your web browser")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
***WebServer-Bridging-Header.h***
|
***WebServer-Bridging-Header.h***
|
||||||
```objectivec
|
```objectivec
|
||||||
#import "GCDWebServer.h"
|
#import <GCDWebServers/GCDWebServer.h>
|
||||||
#import "GCDWebServerDataResponse.h"
|
#import <GCDWebServers/GCDWebServerDataResponse.h>
|
||||||
```
|
```
|
||||||
|
|
||||||
Asynchronous HTTP Responses
|
|
||||||
===========================
|
|
||||||
|
|
||||||
New in GCDWebServer 3.0 is the ability to process HTTP requests aysnchronously i.e. add handlers to the server which generate their ```GCDWebServerResponse``` asynchronously. This is achieved by adding handlers that use a ```GCDWebServerAsyncProcessBlock``` instead of a ```GCDWebServerProcessBlock```. Here's an example:
|
|
||||||
|
|
||||||
**(Synchronous version)** The handler blocks while generating the HTTP response:
|
|
||||||
```objectivec
|
|
||||||
[webServer addDefaultHandlerForMethod:@"GET"
|
|
||||||
requestClass:[GCDWebServerRequest class]
|
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
|
||||||
|
|
||||||
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}];
|
|
||||||
```
|
|
||||||
|
|
||||||
**(Asynchronous version)** The handler returns immediately and calls back GCDWebServer later with the generated HTTP response:
|
|
||||||
```objectivec
|
|
||||||
[webServer addDefaultHandlerForMethod:@"GET"
|
|
||||||
requestClass:[GCDWebServerRequest class]
|
|
||||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
||||||
|
|
||||||
// Do some async operation like network access or file I/O (simulated here using dispatch_after())
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
|
|
||||||
completionBlock(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
}];
|
|
||||||
```
|
|
||||||
|
|
||||||
**(Advanced asynchronous version)** The handler returns immediately a streamed HTTP response which itself generates its contents asynchronously:
|
|
||||||
```objectivec
|
|
||||||
[webServer addDefaultHandlerForMethod:@"GET"
|
|
||||||
requestClass:[GCDWebServerRequest class]
|
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
|
||||||
|
|
||||||
GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/html" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
|
|
||||||
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
completionBlock([@"<html><body><p>Hello" dataUsingEncoding:NSUTF8StringEncoding], nil); // Generate the 1st part of the stream data
|
|
||||||
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
completionBlock([@"World</p></body></html>" dataUsingEncoding:NSUTF8StringEncoding], nil); // Generate the 2nd part of the stream data
|
|
||||||
|
|
||||||
completionBlock([NSData data], nil); // Must pass an empty NSData to signal the end of the stream
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}];
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}];
|
|
||||||
```
|
|
||||||
|
|
||||||
*Note that you can even combine both the asynchronous and advanced asynchronous versions to return asynchronously an asynchronous HTTP response!*
|
|
||||||
|
|
||||||
Web Based Uploads in iOS Apps
|
Web Based Uploads in iOS Apps
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
@@ -274,6 +237,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):**
|
||||||
```objectivec
|
```objectivec
|
||||||
#import "GCDWebServer.h"
|
#import "GCDWebServer.h"
|
||||||
|
|
||||||
@@ -318,6 +282,66 @@ Handlers require 2 GCD blocks:
|
|||||||
|
|
||||||
Note that most methods on ```GCDWebServer``` to add handlers only require the ```GCDWebServerProcessBlock``` or ```GCDWebServerAsyncProcessBlock``` as they already provide a built-in ```GCDWebServerMatchBlock``` e.g. to match a URL path with a Regex.
|
Note that most methods on ```GCDWebServer``` to add handlers only require the ```GCDWebServerProcessBlock``` or ```GCDWebServerAsyncProcessBlock``` as they already provide a built-in ```GCDWebServerMatchBlock``` e.g. to match a URL path with a Regex.
|
||||||
|
|
||||||
|
Asynchronous HTTP Responses
|
||||||
|
===========================
|
||||||
|
|
||||||
|
New in GCDWebServer 3.0 is the ability to process HTTP requests aysnchronously i.e. add handlers to the server which generate their ```GCDWebServerResponse``` asynchronously. This is achieved by adding handlers that use a ```GCDWebServerAsyncProcessBlock``` instead of a ```GCDWebServerProcessBlock```. Here's an example:
|
||||||
|
|
||||||
|
**(Synchronous version)** The handler blocks while generating the HTTP response:
|
||||||
|
```objectivec
|
||||||
|
[webServer addDefaultHandlerForMethod:@"GET"
|
||||||
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
**(Asynchronous version)** The handler returns immediately and calls back GCDWebServer later with the generated HTTP response:
|
||||||
|
```objectivec
|
||||||
|
[webServer addDefaultHandlerForMethod:@"GET"
|
||||||
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
|
|
||||||
|
// Do some async operation like network access or file I/O (simulated here using dispatch_after())
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
|
||||||
|
completionBlock(response);
|
||||||
|
});
|
||||||
|
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
**(Advanced asynchronous version)** The handler returns immediately a streamed HTTP response which itself generates its contents asynchronously:
|
||||||
|
```objectivec
|
||||||
|
[webServer addDefaultHandlerForMethod:@"GET"
|
||||||
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
|
NSMutableArray* contents = [NSMutableArray arrayWithObjects:@"<html><body><p>\n", @"Hello World!\n", @"</p></body></html>\n", nil]; // Fake data source we are reading from
|
||||||
|
GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/html" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
|
||||||
|
|
||||||
|
// Simulate a delay reading from the fake data source
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
NSString* string = contents.firstObject;
|
||||||
|
if (string) {
|
||||||
|
[contents removeObjectAtIndex:0];
|
||||||
|
completionBlock([string dataUsingEncoding:NSUTF8StringEncoding], nil); // Generate the 2nd part of the stream data
|
||||||
|
} else {
|
||||||
|
completionBlock([NSData data], nil); // Must pass an empty NSData to signal the end of the stream
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}];
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
*Note that you can even combine both the asynchronous and advanced asynchronous versions to return asynchronously an asynchronous HTTP response!*
|
||||||
|
|
||||||
GCDWebServer & Background Mode for iOS Apps
|
GCDWebServer & Background Mode for iOS Apps
|
||||||
===========================================
|
===========================================
|
||||||
|
|
||||||
|
|||||||
46
Run-Tests.sh
46
Run-Tests.sh
@@ -1,16 +1,20 @@
|
|||||||
#!/bin/bash -ex
|
#!/bin/bash -ex
|
||||||
|
|
||||||
OSX_SDK="macosx"
|
OSX_SDK="macosx"
|
||||||
if [ -z "$TRAVIS" ]; then
|
IOS_SDK="iphonesimulator"
|
||||||
IOS_SDK="iphoneos"
|
TVOS_SDK="appletvsimulator"
|
||||||
else
|
|
||||||
IOS_SDK="iphonesimulator"
|
OSX_SDK_VERSION=`xcodebuild -version -sdk | grep -A 1 '^MacOSX' | tail -n 1 | awk '{ print $2 }'`
|
||||||
fi
|
IOS_SDK_VERSION=`xcodebuild -version -sdk | grep -A 1 '^iPhoneOS' | tail -n 1 | awk '{ print $2 }'`
|
||||||
|
TVOS_SDK_VERSION=`xcodebuild -version -sdk | grep -A 1 '^AppleTVOS' | tail -n 1 | awk '{ print $2 }'`
|
||||||
|
|
||||||
OSX_TARGET="GCDWebServer (Mac)"
|
OSX_TARGET="GCDWebServer (Mac)"
|
||||||
IOS_TARGET="GCDWebServer (iOS)"
|
IOS_TARGET="GCDWebServer (iOS)"
|
||||||
|
TVOS_TARGET="GCDWebServer (tvOS)"
|
||||||
CONFIGURATION="Release"
|
CONFIGURATION="Release"
|
||||||
|
|
||||||
|
OSX_TEST_SCHEME="GCDWebServers (Mac)"
|
||||||
|
|
||||||
BUILD_DIR="/tmp/GCDWebServer-Build"
|
BUILD_DIR="/tmp/GCDWebServer-Build"
|
||||||
PRODUCT="$BUILD_DIR/$CONFIGURATION/GCDWebServer"
|
PRODUCT="$BUILD_DIR/$CONFIGURATION/GCDWebServer"
|
||||||
|
|
||||||
@@ -30,21 +34,13 @@ function runTests {
|
|||||||
logLevel=2 $1 -mode "$2" -root "$PAYLOAD_DIR/Payload" -tests "$3"
|
logLevel=2 $1 -mode "$2" -root "$PAYLOAD_DIR/Payload" -tests "$3"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build for iOS for oldest deployment target (TODO: run tests on iOS)
|
# Run built-in OS X tests
|
||||||
rm -rf "$BUILD_DIR"
|
rm -rf "$BUILD_DIR"
|
||||||
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" "IPHONEOS_DEPLOYMENT_TARGET=5.1.1" > /dev/null
|
xcodebuild test -scheme "$OSX_TEST_SCHEME" "SYMROOT=$BUILD_DIR"
|
||||||
|
|
||||||
# Build for iOS for default deployment target (TODO: run tests on iOS)
|
# Build for OS X for oldest supported deployment target
|
||||||
rm -rf "$BUILD_DIR"
|
rm -rf "$BUILD_DIR"
|
||||||
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" > /dev/null
|
xcodebuild build -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=10.7" > /dev/null
|
||||||
|
|
||||||
# Build for OS X for oldest deployment target
|
|
||||||
rm -rf "$BUILD_DIR"
|
|
||||||
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=10.7" > /dev/null
|
|
||||||
|
|
||||||
# Build for OS X for default deployment target
|
|
||||||
rm -rf "$BUILD_DIR"
|
|
||||||
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" > /dev/null
|
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
runTests $PRODUCT "htmlForm" "Tests/HTMLForm"
|
runTests $PRODUCT "htmlForm" "Tests/HTMLForm"
|
||||||
@@ -56,5 +52,21 @@ runTests $PRODUCT "webDAV" "Tests/WebDAV-Finder"
|
|||||||
runTests $PRODUCT "webUploader" "Tests/WebUploader"
|
runTests $PRODUCT "webUploader" "Tests/WebUploader"
|
||||||
runTests $PRODUCT "webServer" "Tests/WebServer-Sample-Movie" "Tests/Sample-Movie.mp4"
|
runTests $PRODUCT "webServer" "Tests/WebServer-Sample-Movie" "Tests/Sample-Movie.mp4"
|
||||||
|
|
||||||
|
# Build for OS X for current deployment target
|
||||||
|
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
|
||||||
|
|
||||||
|
# Build for iOS for oldest supported deployment target
|
||||||
|
rm -rf "$BUILD_DIR"
|
||||||
|
xcodebuild build -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "IPHONEOS_DEPLOYMENT_TARGET=6.0" > /dev/null
|
||||||
|
|
||||||
|
# Build for iOS for current deployment target
|
||||||
|
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
|
||||||
|
|
||||||
|
# Build for tvOS for current deployment target
|
||||||
|
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
|
||||||
|
|
||||||
# Done
|
# Done
|
||||||
echo "\nAll tests completed successfully!"
|
echo "\nAll tests completed successfully!"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
@@ -28,5 +28,5 @@
|
|||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
@property(retain, nonatomic) UIWindow* window;
|
@property(strong, nonatomic) UIWindow* window;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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,45 +26,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#import "GCDWebUploader.h"
|
|
||||||
|
|
||||||
@interface AppDelegate () <GCDWebUploaderDelegate> {
|
|
||||||
@private
|
|
||||||
GCDWebUploader* _webServer;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
||||||
CGRect bounds = ([UIScreen instancesRespondToSelector:@selector(nativeBounds)] ? [[UIScreen mainScreen] nativeBounds] : [[UIScreen mainScreen] bounds]);
|
|
||||||
_window = [[UIWindow alloc] initWithFrame:bounds];
|
|
||||||
_window.backgroundColor = [UIColor whiteColor];
|
|
||||||
[_window makeKeyAndVisible];
|
|
||||||
|
|
||||||
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
|
||||||
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
|
|
||||||
_webServer.delegate = self;
|
|
||||||
_webServer.allowHiddenItems = YES;
|
|
||||||
[_webServer start];
|
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (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
|
@end
|
||||||
|
|||||||
68
iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
68
iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "29x29",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "29x29",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "40x40",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "40x40",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "60x60",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "60x60",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"size" : "29x29",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"size" : "29x29",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"size" : "40x40",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"size" : "40x40",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"size" : "76x76",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"size" : "76x76",
|
||||||
|
"scale" : "2x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
28
iOS/Base.lproj/LaunchScreen.storyboard
Normal file
28
iOS/Base.lproj/LaunchScreen.storyboard
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<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">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="EHf-IW-A2E">
|
||||||
|
<objects>
|
||||||
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<animations/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="53" y="375"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
41
iOS/Base.lproj/Main.storyboard
Normal file
41
iOS/Base.lproj/Main.storyboard
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<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">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="tne-QT-ifu">
|
||||||
|
<objects>
|
||||||
|
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<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">
|
||||||
|
<rect key="frame" x="279" y="290" width="42" height="21"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="fqi-2H-Bq5" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="fQm-a5-p9Z"/>
|
||||||
|
<constraint firstItem="fqi-2H-Bq5" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="vB0-cp-Fhd"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="label" destination="fqi-2H-Bq5" id="maJ-eb-cCq"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
@@ -4,24 +4,26 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>${PRODUCT_NAME}</string>
|
|
||||||
<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>net.pol-online.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>${PRODUCT_NAME}</string>
|
<string>$(PRODUCT_NAME)</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIMainStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
<array>
|
<array>
|
||||||
<string>armv7</string>
|
<string>armv7</string>
|
||||||
|
|||||||
31
iOS/ViewController.h
Normal file
31
iOS/ViewController.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
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 ViewController : UIViewController
|
||||||
|
@end
|
||||||
77
iOS/ViewController.m
Normal file
77
iOS/ViewController.m
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
Copyright (c) 2012-2015, 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
|
||||||
|
|||||||
32
tvOS/AppDelegate.h
Normal file
32
tvOS/AppDelegate.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
36
tvOS/AppDelegate.m
Normal file
36
tvOS/AppDelegate.m
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "tv",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"filename" : "Front.imagestacklayer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Middle.imagestacklayer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Back.imagestacklayer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "tv",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "tv",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "tv",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"filename" : "Front.imagestacklayer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Middle.imagestacklayer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Back.imagestacklayer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "tv",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "tv",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"assets" : [
|
||||||
|
{
|
||||||
|
"size" : "1280x768",
|
||||||
|
"idiom" : "tv",
|
||||||
|
"filename" : "App Icon - Large.imagestack",
|
||||||
|
"role" : "primary-app-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "400x240",
|
||||||
|
"idiom" : "tv",
|
||||||
|
"filename" : "App Icon - Small.imagestack",
|
||||||
|
"role" : "primary-app-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "1920x720",
|
||||||
|
"idiom" : "tv",
|
||||||
|
"filename" : "Top Shelf Image.imageset",
|
||||||
|
"role" : "top-shelf-image"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "tv",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
tvOS/Assets.xcassets/Contents.json
Normal file
6
tvOS/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
15
tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
Normal file
15
tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"orientation" : "landscape",
|
||||||
|
"idiom" : "tv",
|
||||||
|
"extent" : "full-screen",
|
||||||
|
"minimum-system-version" : "9.0",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
40
tvOS/Base.lproj/Main.storyboard
Normal file
40
tvOS/Base.lproj/Main.storyboard
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="9059" systemVersion="14F1021" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" initialViewController="BYZ-38-t0r">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="tne-QT-ifu">
|
||||||
|
<objects>
|
||||||
|
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<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">
|
||||||
|
<rect key="frame" x="939" y="530" width="42" height="21"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="IHC-Pp-Jrx" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="BOG-hA-JgS"/>
|
||||||
|
<constraint firstItem="IHC-Pp-Jrx" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="a9B-4C-wVj"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="label" destination="IHC-Pp-Jrx" id="lnE-JP-l00"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
30
tvOS/Info.plist
Normal file
30
tvOS/Info.plist
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>net.pol-online.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>UIMainStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
31
tvOS/ViewController.h
Normal file
31
tvOS/ViewController.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
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 ViewController : UIViewController
|
||||||
|
@end
|
||||||
77
tvOS/ViewController.m
Normal file
77
tvOS/ViewController.m
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
34
tvOS/main.m
Normal file
34
tvOS/main.m
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user