mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-02-11 00:00:07 +08:00
Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43555c6662 | ||
|
|
7c08c54156 | ||
|
|
5834770e08 | ||
|
|
52406a12c2 | ||
|
|
e83814b0fa | ||
|
|
fba593a602 | ||
|
|
eec17d5c7b | ||
|
|
32bc72113a | ||
|
|
200d21c3fa | ||
|
|
3042b853bc | ||
|
|
50eb0437c1 | ||
|
|
00cc560b8e | ||
|
|
fa41f26b30 | ||
|
|
845969ec0d | ||
|
|
629df7895d | ||
|
|
55104e5b1e | ||
|
|
fcc95fdc11 | ||
|
|
10a94e36fd | ||
|
|
010ef9b8bc | ||
|
|
5ca7c27a07 | ||
|
|
c6632633f8 | ||
|
|
5b6eebbb9e | ||
|
|
a4c61bd071 | ||
|
|
2543279a6d | ||
|
|
95231b1a66 | ||
|
|
5f2877b85f | ||
|
|
47a51c3d42 | ||
|
|
3873dd1ad3 | ||
|
|
2ff10258e7 | ||
|
|
4360c4f7db | ||
|
|
8a6a139687 | ||
|
|
4eba86f348 | ||
|
|
ea973735c1 | ||
|
|
5707076e8d | ||
|
|
e1fb807a93 | ||
|
|
71575729e9 | ||
|
|
94e30f6442 | ||
|
|
c98941121a | ||
|
|
4ee9c30911 | ||
|
|
48cf20bb55 | ||
|
|
ac9b8a5f47 | ||
|
|
cad428ca6f | ||
|
|
bb5c1a5195 | ||
|
|
bef95231d2 | ||
|
|
0192c364b6 | ||
|
|
062a0dcee4 | ||
|
|
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 | ||
|
|
628f6673b0 | ||
|
|
1944cd8a6e | ||
|
|
d2001e38ca | ||
|
|
18889793b7 | ||
|
|
14d538b0fb | ||
|
|
3b7198b4cc | ||
|
|
abb891334a | ||
|
|
059f5c8d01 | ||
|
|
9d9546bb6d | ||
|
|
2ff05b1aa0 | ||
|
|
bf2c9a170d | ||
|
|
15caa9cd20 | ||
|
|
32ba49ae34 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ xcuserdata
|
||||
project.xcworkspace
|
||||
|
||||
Tests/Payload
|
||||
Carthage/Build
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
language: objective-c
|
||||
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>
|
||||
22
Frameworks/Info.plist
Normal file
22
Frameworks/Info.plist
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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>CFBundleVersion</key>
|
||||
<string>${BUNDLE_VERSION_STRING}</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.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebDAVServer requires ARC
|
||||
#endif
|
||||
|
||||
// WebDAV specifications: http://webdav.org/specs/rfc4918.html
|
||||
|
||||
// Requires "HEADER_SEARCH_PATHS = $(SDKROOT)/usr/include/libxml2" in Xcode build settings
|
||||
@@ -111,6 +115,11 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
||||
[self.delegate davServer:self didDownloadFileAtPath:absolutePath];
|
||||
});
|
||||
}
|
||||
|
||||
if ([request hasByteRange]) {
|
||||
return [GCDWebServerFileResponse responseWithFile:absolutePath byteRange:request.byteRange];
|
||||
}
|
||||
|
||||
return [GCDWebServerFileResponse responseWithFile:absolutePath];
|
||||
}
|
||||
|
||||
@@ -257,7 +266,10 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
||||
if ((dstRelativePath == nil) || (range.location == NSNotFound)) {
|
||||
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];
|
||||
#pragma clang diagnostic pop
|
||||
NSString* dstAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:dstRelativePath];
|
||||
if (![self _checkSandboxedPath:dstAbsolutePath]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
|
||||
@@ -329,7 +341,10 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
||||
}
|
||||
|
||||
- (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);
|
||||
#pragma clang diagnostic pop
|
||||
if (escapedPath) {
|
||||
NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:itemPath error:NULL];
|
||||
NSString* type = [attributes objectForKey:NSFileType];
|
||||
@@ -418,9 +433,6 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
||||
}
|
||||
if (!success) {
|
||||
NSString* string = [[NSString alloc] initWithData:request.data encoding:NSUTF8StringEncoding];
|
||||
#if !__has_feature(objc_arc)
|
||||
[string autorelease];
|
||||
#endif
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Invalid DAV properties:\n%@", string];
|
||||
}
|
||||
} else {
|
||||
@@ -519,9 +531,6 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
||||
}
|
||||
if (!success) {
|
||||
NSString* string = [[NSString alloc] initWithData:request.data encoding:NSUTF8StringEncoding];
|
||||
#if !__has_feature(objc_arc)
|
||||
[string autorelease];
|
||||
#endif
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Invalid DAV properties:\n%@", string];
|
||||
}
|
||||
|
||||
@@ -604,14 +613,12 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
||||
|
||||
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden;
|
||||
|
||||
@dynamic delegate;
|
||||
|
||||
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
||||
if ((self = [super init])) {
|
||||
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
||||
#if __has_feature(objc_arc)
|
||||
GCDWebDAVServer* __unsafe_unretained server = self;
|
||||
#else
|
||||
__block GCDWebDAVServer* server = self;
|
||||
#endif
|
||||
|
||||
// 9.1 PROPFIND method
|
||||
[self addDefaultHandlerForMethod:@"PROPFIND" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||
@@ -667,17 +674,6 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
||||
return self;
|
||||
}
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
|
||||
- (void)dealloc {
|
||||
[_uploadDirectory release];
|
||||
[_allowedExtensions release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebDAVServer (Subclassing)
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
# http://guides.cocoapods.org/making/getting-setup-with-trunk.html
|
||||
# $ sudo gem update cocoapods
|
||||
# (optional) $ pod trunk register {email} {name} --description={computer}
|
||||
# $ pod trunk push
|
||||
# $ pod trunk --verbose push
|
||||
# DELETE THIS SECTION BEFORE PROCEEDING!
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'GCDWebServer'
|
||||
s.version = '3.1'
|
||||
s.version = '3.3.3'
|
||||
s.author = { 'Pierre-Olivier Latour' => 'info@pol-online.net' }
|
||||
s.license = { :type => 'BSD', :file => 'LICENSE' }
|
||||
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.ios.deployment_target = '5.0'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
s.osx.deployment_target = '10.7'
|
||||
s.requires_arc = true
|
||||
|
||||
@@ -26,24 +27,45 @@ Pod::Spec.new do |s|
|
||||
cs.requires_arc = true
|
||||
cs.ios.library = 'z'
|
||||
cs.ios.frameworks = 'MobileCoreServices', 'CFNetwork'
|
||||
cs.tvos.library = 'z'
|
||||
cs.tvos.frameworks = 'MobileCoreServices', 'CFNetwork'
|
||||
cs.osx.library = 'z'
|
||||
cs.osx.framework = 'SystemConfiguration'
|
||||
end
|
||||
|
||||
s.subspec 'WebDAV' do |cs|
|
||||
s.subspec "CocoaLumberjack" do |cs|
|
||||
cs.dependency 'GCDWebServer/Core'
|
||||
cs.source_files = 'GCDWebDAVServer/*.{h,m}'
|
||||
cs.requires_arc = true
|
||||
cs.ios.library = 'xml2'
|
||||
cs.osx.library = 'xml2'
|
||||
cs.compiler_flags = '-I$(SDKROOT)/usr/include/libxml2'
|
||||
cs.dependency 'CocoaLumberjack', '~> 2'
|
||||
end
|
||||
|
||||
s.subspec 'WebDAV' do |cs|
|
||||
cs.subspec "Core" do |ccs|
|
||||
ccs.dependency 'GCDWebServer/Core'
|
||||
ccs.source_files = 'GCDWebDAVServer/*.{h,m}'
|
||||
ccs.requires_arc = true
|
||||
ccs.ios.library = 'xml2'
|
||||
ccs.tvos.library = 'xml2'
|
||||
ccs.osx.library = 'xml2'
|
||||
ccs.compiler_flags = '-I$(SDKROOT)/usr/include/libxml2'
|
||||
end
|
||||
|
||||
cs.subspec "CocoaLumberjack" do |cscl|
|
||||
cscl.dependency 'GCDWebServer/WebDAV/Core'
|
||||
cscl.dependency 'GCDWebServer/CocoaLumberjack'
|
||||
end
|
||||
end
|
||||
|
||||
s.subspec 'WebUploader' do |cs|
|
||||
cs.dependency 'GCDWebServer/Core'
|
||||
cs.source_files = 'GCDWebUploader/*.{h,m}'
|
||||
cs.requires_arc = true
|
||||
cs.resource = "GCDWebUploader/GCDWebUploader.bundle"
|
||||
end
|
||||
|
||||
cs.subspec "Core" do |ccs|
|
||||
ccs.dependency 'GCDWebServer/Core'
|
||||
ccs.source_files = 'GCDWebUploader/*.{h,m}'
|
||||
ccs.requires_arc = true
|
||||
ccs.resource = "GCDWebUploader/GCDWebUploader.bundle"
|
||||
end
|
||||
|
||||
cs.subspec "CocoaLumberjack" do |cscl|
|
||||
cscl.dependency 'GCDWebServer/WebUploader/Core'
|
||||
cscl.dependency 'GCDWebServer/CocoaLumberjack'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
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"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CEE28CD01AE004D800F4023C"
|
||||
BuildableName = "GCDWebServers.framework"
|
||||
BlueprintName = "GCDWebServers (Mac)"
|
||||
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</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,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
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"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CEE28CEE1AE0051F00F4023C"
|
||||
BuildableName = "GCDWebServers.framework"
|
||||
BlueprintName = "GCDWebServers (iOS)"
|
||||
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</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,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
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"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "E2DDD18A1BE69404002CE867"
|
||||
BuildableName = "GCDWebServers.framework"
|
||||
BlueprintName = "GCDWebServers (tvOS)"
|
||||
ReferencedContainer = "container:GCDWebServer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -52,7 +52,7 @@ typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod,
|
||||
* recommended to return a GCDWebServerErrorResponse on error so more useful
|
||||
* information can be returned to the client.
|
||||
*/
|
||||
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request);
|
||||
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(__kindof GCDWebServerRequest* request);
|
||||
|
||||
/**
|
||||
* The GCDWebServerAsynchronousProcessBlock works like the GCDWebServerProcessBlock
|
||||
@@ -65,7 +65,7 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
|
||||
* useful information can be returned to the client.
|
||||
*/
|
||||
typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* response);
|
||||
typedef void (^GCDWebServerAsyncProcessBlock)(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock);
|
||||
typedef void (^GCDWebServerAsyncProcessBlock)(__kindof GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock);
|
||||
|
||||
/**
|
||||
* The port used by the GCDWebServer (NSNumber / NSUInteger).
|
||||
@@ -79,7 +79,7 @@ extern NSString* const GCDWebServerOption_Port;
|
||||
* the name will automatically take the value of the GCDWebServerOption_ServerName
|
||||
* option. If this option is set to nil, Bonjour will be disabled.
|
||||
*
|
||||
* The default value is an empty string.
|
||||
* The default value is nil.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_BonjourName;
|
||||
|
||||
@@ -90,6 +90,29 @@ extern NSString* const GCDWebServerOption_BonjourName;
|
||||
*/
|
||||
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
|
||||
* network (NSNumber / BOOL).
|
||||
*
|
||||
* The default value is NO.
|
||||
*
|
||||
* @warning Bonjour and NAT port mapping should be disabled if using this option
|
||||
* since the server will not be reachable from the outside network anyway.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_BindToLocalhost;
|
||||
|
||||
/**
|
||||
* The maximum number of incoming HTTP requests that can be queued waiting to
|
||||
* be handled before new ones are dropped (NSNumber / NSUInteger).
|
||||
@@ -153,6 +176,15 @@ extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET;
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval;
|
||||
|
||||
/**
|
||||
* Set the dispatch queue priority on which server connection will be
|
||||
* run (NSNumber / long).
|
||||
*
|
||||
*
|
||||
* The default value is DISPATCH_QUEUE_PRIORITY_DEFAULT.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_DispatchQueuePriority;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
/**
|
||||
@@ -202,9 +234,21 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
/**
|
||||
* This method is called after the Bonjour registration for the server has
|
||||
* successfully completed.
|
||||
*
|
||||
* Use the "bonjourServerURL" property to retrieve the Bonjour address of the
|
||||
* 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
|
||||
* server to serve a series of HTTP requests.
|
||||
@@ -351,6 +395,14 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*/
|
||||
@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)
|
||||
* using the default Bonjour name.
|
||||
@@ -494,12 +546,10 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
* GWS_LOG_INFO(...)
|
||||
* GWS_LOG_WARNING(...)
|
||||
* GWS_LOG_ERROR(...)
|
||||
* GWS_LOG_EXCEPTION(__EXCEPTION__)
|
||||
*
|
||||
* IMPORTANT: Except for GWS_LOG_EXCEPTION() which gets passed an NSException,
|
||||
* these macros must behave like NSLog(). Furthermore the GWS_LOG_DEBUG() macro
|
||||
* should not do anything unless the preprocessor constant "DEBUG" evaluates to
|
||||
* non-zero.
|
||||
* IMPORTANT: These macros must behave like NSLog(). Furthermore the GWS_LOG_DEBUG()
|
||||
* macro should not do anything unless the preprocessor constant "DEBUG" evaluates
|
||||
* to non-zero.
|
||||
*
|
||||
* The logging methods below send log messages to the same logging facility
|
||||
* used by GCDWebServer. They can be used for consistency wherever you interact
|
||||
@@ -512,6 +562,13 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*
|
||||
* @warning The interpretation of the "level" argument depends on the logging
|
||||
* facility used at compile time.
|
||||
*
|
||||
* If using the built-in logging facility, the log levels are as follow:
|
||||
* DEBUG = 0
|
||||
* VERBOSE = 1
|
||||
* INFO = 2
|
||||
* WARNING = 3
|
||||
* ERROR = 4
|
||||
*/
|
||||
+ (void)setLogLevel:(int)level;
|
||||
|
||||
@@ -535,11 +592,6 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*/
|
||||
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
||||
|
||||
/**
|
||||
* Logs an exception to the logging facility at the EXCEPTION level.
|
||||
*/
|
||||
- (void)logException:(NSException*)exception;
|
||||
|
||||
@end
|
||||
|
||||
#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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
@@ -34,6 +38,7 @@
|
||||
#endif
|
||||
#endif
|
||||
#import <netinet/in.h>
|
||||
#import <dns_sd.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@@ -43,9 +48,13 @@
|
||||
#define kDefaultPort 8080
|
||||
#endif
|
||||
|
||||
#define kBonjourResolutionTimeout 5.0
|
||||
|
||||
NSString* const GCDWebServerOption_Port = @"Port";
|
||||
NSString* const GCDWebServerOption_BonjourName = @"BonjourName";
|
||||
NSString* const GCDWebServerOption_BonjourType = @"BonjourType";
|
||||
NSString* const GCDWebServerOption_RequestNATPortMapping = @"RequestNATPortMapping";
|
||||
NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost";
|
||||
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
|
||||
NSString* const GCDWebServerOption_ServerName = @"ServerName";
|
||||
NSString* const GCDWebServerOption_AuthenticationMethod = @"AuthenticationMethod";
|
||||
@@ -54,6 +63,7 @@ NSString* const GCDWebServerOption_AuthenticationAccounts = @"AuthenticationAcco
|
||||
NSString* const GCDWebServerOption_ConnectionClass = @"ConnectionClass";
|
||||
NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET = @"AutomaticallyMapHEADToGET";
|
||||
NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval = @"ConnectedStateCoalescingInterval";
|
||||
NSString* const GCDWebServerOption_DispatchQueuePriority = @"DispatchQueuePriority";
|
||||
#if TARGET_OS_IPHONE
|
||||
NSString* const GCDWebServerOption_AutomaticallySuspendInBackground = @"AutomaticallySuspendInBackground";
|
||||
#endif
|
||||
@@ -69,9 +79,9 @@ GCDWebServerLoggingLevel GCDWebServerLogLevel = kGCDWebServerLoggingLevel_Info;
|
||||
#endif
|
||||
#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__)
|
||||
#if DEBUG
|
||||
int GCDWebServerLogLevel = LOG_LEVEL_DEBUG;
|
||||
DDLogLevel GCDWebServerLogLevel = DDLogLevelDebug;
|
||||
#else
|
||||
int GCDWebServerLogLevel = LOG_LEVEL_INFO;
|
||||
DDLogLevel GCDWebServerLogLevel = DDLogLevelInfo;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -82,7 +92,7 @@ static BOOL _run;
|
||||
#ifdef __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__
|
||||
|
||||
void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) {
|
||||
static const char* levelNames[] = {"DEBUG", "VERBOSE", "INFO", "WARNING", "ERROR", "EXCEPTION"};
|
||||
static const char* levelNames[] = {"DEBUG", "VERBOSE", "INFO", "WARNING", "ERROR"};
|
||||
static int enableLogging = -1;
|
||||
if (enableLogging < 0) {
|
||||
enableLogging = (isatty(STDERR_FILENO) ? 1 : 0);
|
||||
@@ -93,7 +103,6 @@ void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ..
|
||||
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
fprintf(stderr, "[%s] %s\n", levelNames[level], [message UTF8String]);
|
||||
ARC_RELEASE(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,13 +151,6 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_matchBlock);
|
||||
ARC_RELEASE(_asyncProcessBlock);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServer () {
|
||||
@@ -169,11 +171,18 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
Class _connectionClass;
|
||||
BOOL _mapHEADToGET;
|
||||
CFTimeInterval _disconnectDelay;
|
||||
dispatch_queue_priority_t _dispatchQueuePriority;
|
||||
NSUInteger _port;
|
||||
dispatch_source_t _source4;
|
||||
dispatch_source_t _source6;
|
||||
CFNetServiceRef _registrationService;
|
||||
CFNetServiceRef _resolutionService;
|
||||
DNSServiceRef _dnsService;
|
||||
CFSocketRef _dnsSocket;
|
||||
CFRunLoopSourceRef _dnsSource;
|
||||
NSString* _dnsAddress;
|
||||
NSUInteger _dnsPort;
|
||||
BOOL _bindToLocalhost;
|
||||
#if TARGET_OS_IPHONE
|
||||
BOOL _suspendInBackground;
|
||||
UIBackgroundTaskIdentifier _backgroundTask;
|
||||
@@ -188,7 +197,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
|
||||
@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm,
|
||||
authenticationBasicAccounts=_authenticationBasicAccounts, authenticationDigestAccounts=_authenticationDigestAccounts,
|
||||
shouldAutomaticallyMapHEADToGET=_mapHEADToGET;
|
||||
shouldAutomaticallyMapHEADToGET=_mapHEADToGET, dispatchQueuePriority=_dispatchQueuePriority;
|
||||
|
||||
+ (void)initialize {
|
||||
GCDWebServerInitializeFunctions();
|
||||
@@ -212,11 +221,10 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
GWS_DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
|
||||
GWS_DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle
|
||||
|
||||
ARC_RELEASE(_handlers);
|
||||
ARC_DISPATCH_RELEASE(_sourceGroup);
|
||||
ARC_DISPATCH_RELEASE(_syncQueue);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
dispatch_release(_sourceGroup);
|
||||
dispatch_release(_syncQueue);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
@@ -247,7 +255,9 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
GWS_LOG_DEBUG(@"Did connect");
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
[self _startBackgroundTask];
|
||||
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
||||
[self _startBackgroundTask];
|
||||
}
|
||||
#endif
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
|
||||
@@ -288,8 +298,6 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
|
||||
_backgroundTask = UIBackgroundTaskInvalid;
|
||||
GWS_LOG_DEBUG(@"Did end background task");
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,12 +347,12 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
|
||||
- (NSString*)bonjourName {
|
||||
CFStringRef name = _resolutionService ? CFNetServiceGetName(_resolutionService) : NULL;
|
||||
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
|
||||
return name && CFStringGetLength(name) ? CFBridgingRelease(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
|
||||
}
|
||||
|
||||
- (NSString*)bonjourType {
|
||||
CFStringRef type = _resolutionService ? CFNetServiceGetType(_resolutionService) : NULL;
|
||||
return type && CFStringGetLength(type) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, type)) : nil;
|
||||
return type && CFStringGetLength(type) ? CFBridgingRelease(CFStringCreateCopy(kCFAllocatorDefault, type)) : nil;
|
||||
}
|
||||
|
||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
||||
@@ -357,7 +365,6 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
GWS_DCHECK(_options == nil);
|
||||
GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock asyncProcessBlock:processBlock];
|
||||
[_handlers insertObject:handler atIndex:0];
|
||||
ARC_RELEASE(handler);
|
||||
}
|
||||
|
||||
- (void)removeAllHandlers {
|
||||
@@ -371,9 +378,12 @@ static void _NetServiceRegisterCallBack(CFNetServiceRef service, CFStreamError*
|
||||
if (error->error) {
|
||||
GWS_LOG_ERROR(@"Bonjour registration error %i (domain %i)", (int)error->error, (int)error->domain);
|
||||
} else {
|
||||
GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info;
|
||||
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,8 +396,8 @@ static void _NetServiceResolveCallBack(CFNetServiceRef service, CFStreamError* e
|
||||
GWS_LOG_ERROR(@"Bonjour resolution error %i (domain %i)", (int)error->error, (int)error->domain);
|
||||
}
|
||||
} else {
|
||||
GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info;
|
||||
GWS_LOG_INFO(@"%@ now reachable at %@", [server class], server.bonjourServerURL);
|
||||
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
||||
GWS_LOG_INFO(@"%@ now locally reachable at %@", [server class], server.bonjourServerURL);
|
||||
if ([server.delegate respondsToSelector:@selector(webServerDidCompleteBonjourRegistration:)]) {
|
||||
[server.delegate webServerDidCompleteBonjourRegistration:server];
|
||||
}
|
||||
@@ -395,6 +405,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) {
|
||||
id value = [options objectForKey:key];
|
||||
return value ? value : defaultValue;
|
||||
@@ -407,7 +452,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
return [data base64Encoding];
|
||||
}
|
||||
#endif
|
||||
return ARC_AUTORELEASE([[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding]);
|
||||
return [[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding];
|
||||
}
|
||||
|
||||
- (int)_createListeningSocket:(BOOL)useIPv6
|
||||
@@ -450,7 +495,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
|
||||
- (dispatch_source_t)_createDispatchSourceWithListeningSocket:(int)listeningSocket isIPv6:(BOOL)isIPv6 {
|
||||
dispatch_group_enter(_sourceGroup);
|
||||
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, kGCDWebServerGCDQueue);
|
||||
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0));
|
||||
dispatch_source_set_cancel_handler(source, ^{
|
||||
|
||||
@autoreleasepool {
|
||||
@@ -467,18 +512,18 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
dispatch_source_set_event_handler(source, ^{
|
||||
|
||||
@autoreleasepool {
|
||||
struct sockaddr remoteSockAddr;
|
||||
struct sockaddr_storage remoteSockAddr;
|
||||
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
|
||||
int socket = accept(listeningSocket, &remoteSockAddr, &remoteAddrLen);
|
||||
int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen);
|
||||
if (socket > 0) {
|
||||
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
|
||||
|
||||
struct sockaddr localSockAddr;
|
||||
struct sockaddr_storage localSockAddr;
|
||||
socklen_t localAddrLen = sizeof(localSockAddr);
|
||||
NSData* localAddress = nil;
|
||||
if (getsockname(socket, &localSockAddr, &localAddrLen) == 0) {
|
||||
if (getsockname(socket, (struct sockaddr*)&localSockAddr, &localAddrLen) == 0) {
|
||||
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 {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
@@ -487,11 +532,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE
|
||||
|
||||
GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
|
||||
#if __has_feature(objc_arc)
|
||||
[connection self]; // Prevent compiler from complaining about unused variable / useless statement
|
||||
#else
|
||||
[connection release];
|
||||
#endif
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
||||
}
|
||||
@@ -505,6 +546,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
GWS_DCHECK(_source4 == NULL);
|
||||
|
||||
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
||||
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
|
||||
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
|
||||
|
||||
struct sockaddr_in addr4;
|
||||
@@ -512,17 +554,16 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
addr4.sin_len = sizeof(addr4);
|
||||
addr4.sin_family = AF_INET;
|
||||
addr4.sin_port = htons(port);
|
||||
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr4.sin_addr.s_addr = bindToLocalhost ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY);
|
||||
int listeningSocket4 = [self _createListeningSocket:NO localAddress:&addr4 length:sizeof(addr4) maxPendingConnections:maxPendingConnections error:error];
|
||||
if (listeningSocket4 <= 0) {
|
||||
return NO;
|
||||
}
|
||||
if (port == 0) {
|
||||
struct sockaddr addr;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
if (getsockname(listeningSocket4, &addr, &addrlen) == 0) {
|
||||
struct sockaddr_in* sockaddr = (struct sockaddr_in*)&addr;
|
||||
port = ntohs(sockaddr->sin_port);
|
||||
if (getsockname(listeningSocket4, (struct sockaddr*)&addr, &addrlen) == 0) {
|
||||
port = ntohs(addr.sin_port);
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
|
||||
}
|
||||
@@ -533,7 +574,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
addr6.sin6_len = sizeof(addr6);
|
||||
addr6.sin6_family = AF_INET6;
|
||||
addr6.sin6_port = htons(port);
|
||||
addr6.sin6_addr = in6addr_any;
|
||||
addr6.sin6_addr = bindToLocalhost ? in6addr_loopback : in6addr_any;
|
||||
int listeningSocket6 = [self _createListeningSocket:YES localAddress:&addr6 length:sizeof(addr6) maxPendingConnections:maxPendingConnections error:error];
|
||||
if (listeningSocket6 <= 0) {
|
||||
close(listeningSocket4);
|
||||
@@ -560,17 +601,19 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
|
||||
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
|
||||
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
|
||||
_dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue];
|
||||
|
||||
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
|
||||
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
|
||||
_port = port;
|
||||
_bindToLocalhost = bindToLocalhost;
|
||||
|
||||
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, @"");
|
||||
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil);
|
||||
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
||||
if (bonjourName) {
|
||||
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (ARC_BRIDGE CFStringRef)bonjourType, (ARC_BRIDGE CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port);
|
||||
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (__bridge CFStringRef)bonjourType, (__bridge CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port);
|
||||
if (_registrationService) {
|
||||
CFNetServiceClientContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
|
||||
CFNetServiceClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
||||
|
||||
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
|
||||
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||
@@ -581,9 +624,34 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
if (_resolutionService) {
|
||||
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
||||
CFNetServiceScheduleWithRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed creating CFNetService for resolution");
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,6 +670,22 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
- (void)_stop {
|
||||
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 (_resolutionService) {
|
||||
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||
@@ -620,19 +704,20 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
dispatch_source_cancel(_source6);
|
||||
dispatch_source_cancel(_source4);
|
||||
dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed
|
||||
ARC_DISPATCH_RELEASE(_source6);
|
||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
dispatch_release(_source6);
|
||||
#endif
|
||||
_source6 = NULL;
|
||||
ARC_DISPATCH_RELEASE(_source4);
|
||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
dispatch_release(_source4);
|
||||
#endif
|
||||
_source4 = NULL;
|
||||
_port = 0;
|
||||
_bindToLocalhost = NO;
|
||||
|
||||
ARC_RELEASE(_serverName);
|
||||
_serverName = nil;
|
||||
ARC_RELEASE(_authenticationRealm);
|
||||
_authenticationRealm = nil;
|
||||
ARC_RELEASE(_authenticationBasicAccounts);
|
||||
_authenticationBasicAccounts = nil;
|
||||
ARC_RELEASE(_authenticationDigestAccounts);
|
||||
_authenticationDigestAccounts = nil;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@@ -674,7 +759,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
|
||||
- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error {
|
||||
if (_options == nil) {
|
||||
_options = [options copy];
|
||||
_options = options ? [options copy] : @{};
|
||||
#if TARGET_OS_IPHONE
|
||||
_suspendInBackground = [_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue];
|
||||
if (((_suspendInBackground == NO) || ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground)) && ![self _start:error])
|
||||
@@ -682,7 +767,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
if (![self _start:error])
|
||||
#endif
|
||||
{
|
||||
ARC_RELEASE(_options);
|
||||
_options = nil;
|
||||
return NO;
|
||||
}
|
||||
@@ -714,7 +798,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
if (_source4) {
|
||||
[self _stop];
|
||||
}
|
||||
ARC_RELEASE(_options);
|
||||
_options = nil;
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
@@ -727,7 +810,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
|
||||
- (NSURL*)serverURL {
|
||||
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 (_port != 80) {
|
||||
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", ipAddress, (int)_port]];
|
||||
@@ -741,7 +824,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
|
||||
- (NSURL*)bonjourServerURL {
|
||||
if (_source4 && _resolutionService) {
|
||||
NSString* name = (ARC_BRIDGE NSString*)CFNetServiceGetTargetHost(_resolutionService);
|
||||
NSString* name = (__bridge NSString*)CFNetServiceGetTargetHost(_resolutionService);
|
||||
if (name.length) {
|
||||
name = [name substringToIndex:(name.length - 1)]; // Strip trailing period at end of domain
|
||||
if (_port != 80) {
|
||||
@@ -754,6 +837,17 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
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 {
|
||||
return [self startWithPort:kDefaultPort bonjourName:@""];
|
||||
}
|
||||
@@ -813,7 +907,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
if (![requestMethod isEqualToString:method]) {
|
||||
return nil;
|
||||
}
|
||||
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||
|
||||
} asyncProcessBlock:block];
|
||||
}
|
||||
@@ -834,7 +928,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
if ([urlPath caseInsensitiveCompare:path] != NSOrderedSame) {
|
||||
return nil;
|
||||
}
|
||||
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||
|
||||
} asyncProcessBlock:block];
|
||||
} else {
|
||||
@@ -866,13 +960,18 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
for (NSTextCheckingResult* result in matches) {
|
||||
// Start at 1; index 0 is the whole string
|
||||
for (NSUInteger i = 1; i < result.numberOfRanges; i++) {
|
||||
[captures addObject:[urlPath substringWithRange:[result rangeAtIndex:i]]];
|
||||
NSRange range = [result rangeAtIndex:i];
|
||||
// range is {NSNotFound, 0} "if one of the capture groups did not participate in this particular match"
|
||||
// see discussion in -[NSRegularExpression firstMatchInString:options:range:]
|
||||
if (range.location != NSNotFound) {
|
||||
[captures addObject:[urlPath substringWithRange:range]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
|
||||
return ARC_AUTORELEASE(request);
|
||||
return request;
|
||||
|
||||
} asyncProcessBlock:block];
|
||||
} else {
|
||||
@@ -922,7 +1021,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
for (NSString* file in enumerator) {
|
||||
if (![file hasPrefix:@"."]) {
|
||||
NSString* type = [[enumerator fileAttributes] objectForKey:NSFileType];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
NSString* escapedFile = [file stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
#pragma clang diagnostic pop
|
||||
GWS_DCHECK(escapedFile);
|
||||
if ([type isEqualToString:NSFileTypeRegular]) {
|
||||
[html appendFormat:@"<li><a href=\"%@\">%@</a></li>\n", escapedFile, file];
|
||||
@@ -939,11 +1041,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
|
||||
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
||||
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
|
||||
#if __has_feature(objc_arc)
|
||||
GCDWebServer* __unsafe_unretained server = self;
|
||||
#else
|
||||
__block GCDWebServer* server = self;
|
||||
#endif
|
||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||
|
||||
if (![requestMethod isEqualToString:@"GET"]) {
|
||||
@@ -952,7 +1050,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
if (![urlPath hasPrefix:basePath]) {
|
||||
return nil;
|
||||
}
|
||||
return ARC_AUTORELEASE([[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||
return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||
|
||||
} processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||
|
||||
@@ -1000,7 +1098,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
[XLSharedFacility setMinLogLevel:level];
|
||||
#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__)
|
||||
GCDWebServerLogLevel = level;
|
||||
#else
|
||||
#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__)
|
||||
GCDWebServerLogLevel = level;
|
||||
#endif
|
||||
}
|
||||
@@ -1008,35 +1106,31 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
- (void)logVerbose:(NSString*)format, ... {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GWS_LOG_VERBOSE(@"%@", ARC_AUTORELEASE([[NSString alloc] initWithFormat:format arguments:arguments]));
|
||||
GWS_LOG_VERBOSE(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
- (void)logInfo:(NSString*)format, ... {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GWS_LOG_INFO(@"%@", ARC_AUTORELEASE([[NSString alloc] initWithFormat:format arguments:arguments]));
|
||||
GWS_LOG_INFO(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
- (void)logWarning:(NSString*)format, ... {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GWS_LOG_WARNING(@"%@", ARC_AUTORELEASE([[NSString alloc] initWithFormat:format arguments:arguments]));
|
||||
GWS_LOG_WARNING(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
- (void)logError:(NSString*)format, ... {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GWS_LOG_ERROR(@"%@", ARC_AUTORELEASE([[NSString alloc] initWithFormat:format arguments:arguments]));
|
||||
GWS_LOG_ERROR(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
- (void)logException:(NSException*)exception {
|
||||
GWS_LOG_EXCEPTION(exception);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
@@ -1093,7 +1187,6 @@ static CFHTTPMessageRef _CreateHTTPMessageFromPerformingRequest(NSData* inData,
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
ARC_RELEASE(outData);
|
||||
}
|
||||
}
|
||||
close(httpSocket);
|
||||
@@ -1107,7 +1200,6 @@ static void _LogResult(NSString* format, ...) {
|
||||
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
fprintf(stdout, "%s\n", [message UTF8String]);
|
||||
ARC_RELEASE(message);
|
||||
}
|
||||
|
||||
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path {
|
||||
@@ -1130,8 +1222,8 @@ static void _LogResult(NSString* format, ...) {
|
||||
if (requestData) {
|
||||
CFHTTPMessageRef request = _CreateHTTPMessageFromData(requestData, YES);
|
||||
if (request) {
|
||||
NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(request));
|
||||
NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(request));
|
||||
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(request));
|
||||
NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(request));
|
||||
_LogResult(@"[%i] %@ %@", (int)[index integerValue], requestMethod, requestURL.path);
|
||||
NSString* prefix = [index stringByAppendingString:@"-"];
|
||||
for (NSString* responseFile in files) {
|
||||
@@ -1151,8 +1243,8 @@ static void _LogResult(NSString* format, ...) {
|
||||
success = NO;
|
||||
}
|
||||
|
||||
NSDictionary* expectedHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
||||
NSDictionary* actualHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
||||
NSDictionary* expectedHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
||||
NSDictionary* actualHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
||||
for (NSString* expectedHeader in expectedHeaders) {
|
||||
if ([ignoredHeaders containsObject:expectedHeader]) {
|
||||
continue;
|
||||
@@ -1171,10 +1263,10 @@ static void _LogResult(NSString* format, ...) {
|
||||
}
|
||||
}
|
||||
|
||||
NSString* expectedContentLength = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length")));
|
||||
NSData* expectedBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(expectedResponse));
|
||||
NSString* actualContentLength = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length")));
|
||||
NSData* actualBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(actualResponse));
|
||||
NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length")));
|
||||
NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse));
|
||||
NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length")));
|
||||
NSData* actualBody = CFBridgingRelease(CFHTTPMessageCopyBody(actualResponse));
|
||||
if ([actualContentLength isEqualToString:expectedContentLength] && (actualBody.length > expectedBody.length)) { // Handle web browser closing connection before retrieving entire body (e.g. when playing a video file)
|
||||
actualBody = [actualBody subdataWithRange:NSMakeRange(0, expectedBody.length)];
|
||||
}
|
||||
@@ -1191,7 +1283,6 @@ static void _LogResult(NSString* format, ...) {
|
||||
[task setLaunchPath:@"/usr/bin/opendiff"];
|
||||
[task setArguments:@[expectedPath, actualPath]];
|
||||
[task launch];
|
||||
ARC_RELEASE(task);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#import <netdb.h>
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
@@ -84,7 +88,7 @@ static int32_t _connectionCounter = 0;
|
||||
@implementation GCDWebServerConnection (Read)
|
||||
|
||||
- (void)_readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
|
||||
dispatch_read(_socket, length, kGCDWebServerGCDQueue, ^(dispatch_data_t buffer, int error) {
|
||||
dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
|
||||
|
||||
@autoreleasepool {
|
||||
if (error == 0) {
|
||||
@@ -172,7 +176,6 @@ static int32_t _connectionCounter = 0;
|
||||
}
|
||||
|
||||
}];
|
||||
ARC_RELEASE(bodyData);
|
||||
}
|
||||
|
||||
static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
@@ -244,17 +247,10 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
@implementation GCDWebServerConnection (Write)
|
||||
|
||||
- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
|
||||
#if !__has_feature(objc_arc)
|
||||
[data retain];
|
||||
#endif
|
||||
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, kGCDWebServerGCDQueue, ^{
|
||||
#if __has_feature(objc_arc)
|
||||
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^{
|
||||
[data self]; // Keeps ARC from releasing data too early
|
||||
#else
|
||||
[data release];
|
||||
#endif
|
||||
});
|
||||
dispatch_write(_socket, buffer, kGCDWebServerGCDQueue, ^(dispatch_data_t remainingData, int error) {
|
||||
dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
|
||||
|
||||
@autoreleasepool {
|
||||
if (error == 0) {
|
||||
@@ -268,13 +264,15 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
}
|
||||
|
||||
});
|
||||
ARC_DISPATCH_RELEASE(buffer);
|
||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
dispatch_release(buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
|
||||
GWS_DCHECK(_responseMessage);
|
||||
CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage);
|
||||
[self _writeData:(ARC_BRIDGE NSData*)data withCompletionBlock:block];
|
||||
[self _writeData:(__bridge NSData*)data withCompletionBlock:block];
|
||||
CFRelease(data);
|
||||
}
|
||||
|
||||
@@ -349,11 +347,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
}
|
||||
if (_continueData == nil) {
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1);
|
||||
#if __has_feature(objc_arc)
|
||||
_continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message));
|
||||
#else
|
||||
_continueData = (NSData*)CFHTTPMessageCopySerializedMessage(message);
|
||||
#endif
|
||||
CFRelease(message);
|
||||
GWS_DCHECK(_continueData);
|
||||
}
|
||||
@@ -362,7 +356,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
}
|
||||
if (_digestAuthenticationNonce == nil) {
|
||||
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
|
||||
_digestAuthenticationNonce = ARC_RETAIN(GCDWebServerComputeMD5Digest(@"%@", ARC_BRIDGE_RELEASE(CFUUIDCreateString(kCFAllocatorDefault, uuid))));
|
||||
_digestAuthenticationNonce = GCDWebServerComputeMD5Digest(@"%@", CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid)));
|
||||
CFRelease(uuid);
|
||||
}
|
||||
}
|
||||
@@ -376,8 +370,8 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
_statusCode = statusCode;
|
||||
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close"));
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (ARC_BRIDGE CFStringRef)_server.serverName);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (ARC_BRIDGE CFStringRef)GCDWebServerFormatRFC822([NSDate date]));
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (__bridge CFStringRef)_server.serverName);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (__bridge CFStringRef)GCDWebServerFormatRFC822([NSDate date]));
|
||||
}
|
||||
|
||||
- (void)_startProcessingRequest {
|
||||
@@ -410,36 +404,36 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
if (hasBody && ![response performOpen:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error);
|
||||
} else {
|
||||
_response = ARC_RETAIN(response);
|
||||
_response = response;
|
||||
}
|
||||
}
|
||||
|
||||
if (_response) {
|
||||
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
||||
if (_response.lastModifiedDate) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (ARC_BRIDGE CFStringRef)GCDWebServerFormatRFC822(_response.lastModifiedDate));
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822(_response.lastModifiedDate));
|
||||
}
|
||||
if (_response.eTag) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (ARC_BRIDGE CFStringRef)_response.eTag);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag);
|
||||
}
|
||||
if ((_response.statusCode >= 200) && (_response.statusCode < 300)) {
|
||||
if (_response.cacheControlMaxAge > 0) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (ARC_BRIDGE CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (__bridge CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]);
|
||||
} else {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache"));
|
||||
}
|
||||
}
|
||||
if (_response.contentType != nil) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (ARC_BRIDGE CFStringRef)GCDWebServerNormalizeHeaderValue(_response.contentType));
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (__bridge CFStringRef)GCDWebServerNormalizeHeaderValue(_response.contentType));
|
||||
}
|
||||
if (_response.contentLength != NSUIntegerMax) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (ARC_BRIDGE CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (__bridge CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]);
|
||||
}
|
||||
if (_response.usesChunkedTransferEncoding) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked"));
|
||||
}
|
||||
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (ARC_BRIDGE CFStringRef)key, (ARC_BRIDGE CFStringRef)obj);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
||||
}];
|
||||
[self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
||||
|
||||
@@ -524,7 +518,6 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
}
|
||||
|
||||
}];
|
||||
ARC_RELEASE(chunkData);
|
||||
}
|
||||
|
||||
- (void)_readRequestHeaders {
|
||||
@@ -533,28 +526,30 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
[self _readHeaders:headersData withCompletionBlock:^(NSData* extraData) {
|
||||
|
||||
if (extraData) {
|
||||
NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase
|
||||
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase
|
||||
if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
|
||||
requestMethod = @"GET";
|
||||
_virtualHEAD = YES;
|
||||
}
|
||||
NSDictionary* requestHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
|
||||
NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(_requestMessage));
|
||||
NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
|
||||
NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage));
|
||||
if (requestURL) {
|
||||
requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
|
||||
GWS_DCHECK(requestURL);
|
||||
}
|
||||
NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(ARC_BRIDGE_RELEASE(CFURLCopyPath((CFURLRef)requestURL))) : nil; // Don't use -[NSURL path] which strips the ending slash
|
||||
NSString* queryString = requestURL ? ARC_BRIDGE_RELEASE(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
|
||||
NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL))) : nil; // Don't use -[NSURL path] which strips the ending slash
|
||||
NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
|
||||
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
|
||||
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
|
||||
for (_handler in _server.handlers) {
|
||||
_request = ARC_RETAIN(_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery));
|
||||
_request = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
|
||||
if (_request) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_request) {
|
||||
_request.localAddressData = self.localAddressData;
|
||||
_request.remoteAddressData = self.remoteAddressData;
|
||||
if ([_request hasBody]) {
|
||||
[_request prepareForWriting];
|
||||
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
|
||||
@@ -604,14 +599,13 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
}
|
||||
|
||||
}];
|
||||
ARC_RELEASE(headersData);
|
||||
}
|
||||
|
||||
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
|
||||
if ((self = [super init])) {
|
||||
_server = ARC_RETAIN(server);
|
||||
_localAddress = ARC_RETAIN(localAddress);
|
||||
_remoteAddress = ARC_RETAIN(remoteAddress);
|
||||
_server = server;
|
||||
_localAddress = localAddress;
|
||||
_remoteAddress = remoteAddress;
|
||||
_socket = socket;
|
||||
GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
||||
|
||||
@@ -619,7 +613,6 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
|
||||
if (![self open]) {
|
||||
close(_socket);
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
_opened = YES;
|
||||
@@ -650,26 +643,14 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
}
|
||||
|
||||
[_server didEndConnection:self];
|
||||
ARC_RELEASE(_server);
|
||||
ARC_RELEASE(_localAddress);
|
||||
ARC_RELEASE(_remoteAddress);
|
||||
|
||||
if (_requestMessage) {
|
||||
CFRelease(_requestMessage);
|
||||
}
|
||||
ARC_RELEASE(_request);
|
||||
|
||||
if (_responseMessage) {
|
||||
CFRelease(_responseMessage);
|
||||
}
|
||||
ARC_RELEASE(_response);
|
||||
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
ARC_RELEASE(_requestPath);
|
||||
ARC_RELEASE(_responsePath);
|
||||
#endif
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -681,11 +662,11 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
if (_server.recordingEnabled) {
|
||||
_connectionIndex = OSAtomicIncrement32(&_connectionCounter);
|
||||
|
||||
_requestPath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
|
||||
_requestPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
GWS_DCHECK(_requestFD > 0);
|
||||
|
||||
_responsePath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
|
||||
_responsePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
GWS_DCHECK(_responseFD > 0);
|
||||
}
|
||||
@@ -777,23 +758,22 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
|
||||
- (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);
|
||||
@try {
|
||||
_handler.asyncProcessBlock(request, completion);
|
||||
}
|
||||
@catch (NSException* exception) {
|
||||
GWS_LOG_EXCEPTION(exception);
|
||||
}
|
||||
_handler.asyncProcessBlock(request, [completion copy]);
|
||||
}
|
||||
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
|
||||
static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) {
|
||||
if ([requestETag isEqualToString:@"*"] && (!responseLastModified || !requestLastModified || ([responseLastModified compare:requestLastModified] != NSOrderedDescending))) {
|
||||
return YES;
|
||||
} else {
|
||||
if ([responseETag isEqualToString:requestETag]) {
|
||||
if (requestLastModified && responseLastModified) {
|
||||
if ([responseLastModified compare:requestLastModified] != NSOrderedDescending) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
@@ -50,14 +54,14 @@ void GCDWebServerInitializeFunctions() {
|
||||
_dateFormatterRFC822 = [[NSDateFormatter alloc] init];
|
||||
_dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
||||
_dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
|
||||
_dateFormatterRFC822.locale = ARC_AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]);
|
||||
_dateFormatterRFC822.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
|
||||
GWS_DCHECK(_dateFormatterRFC822);
|
||||
}
|
||||
if (_dateFormatterISO8601 == nil) {
|
||||
_dateFormatterISO8601 = [[NSDateFormatter alloc] init];
|
||||
_dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
||||
_dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'";
|
||||
_dateFormatterISO8601.locale = ARC_AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]);
|
||||
_dateFormatterISO8601.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
|
||||
GWS_DCHECK(_dateFormatterISO8601);
|
||||
}
|
||||
if (_dateFormatterQueue == NULL) {
|
||||
@@ -96,7 +100,6 @@ NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* nam
|
||||
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter];
|
||||
}
|
||||
}
|
||||
ARC_RELEASE(scanner);
|
||||
return parameter;
|
||||
}
|
||||
|
||||
@@ -150,7 +153,7 @@ NSString* GCDWebServerDescribeData(NSData* data, NSString* type) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
if (string) {
|
||||
return ARC_AUTORELEASE(string);
|
||||
return string;
|
||||
}
|
||||
}
|
||||
return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length];
|
||||
@@ -168,9 +171,9 @@ NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
||||
if (extension.length) {
|
||||
mimeType = [_overrides objectForKey:extension];
|
||||
if (mimeType == nil) {
|
||||
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (ARC_BRIDGE CFStringRef)extension, NULL);
|
||||
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
|
||||
if (uti) {
|
||||
mimeType = ARC_BRIDGE_RELEASE(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
|
||||
mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
|
||||
CFRelease(uti);
|
||||
}
|
||||
}
|
||||
@@ -179,11 +182,17 @@ NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
||||
}
|
||||
|
||||
NSString* GCDWebServerEscapeURLString(NSString* string) {
|
||||
return ARC_BRIDGE_RELEASE(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8));
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8));
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
NSString* GCDWebServerUnescapeURLString(NSString* string) {
|
||||
return ARC_BRIDGE_RELEASE(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8));
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8));
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||
@@ -219,7 +228,6 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||
}
|
||||
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
||||
}
|
||||
ARC_RELEASE(scanner);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@@ -238,7 +246,7 @@ NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL inclu
|
||||
NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
NSString* address = nil;
|
||||
#if TARGET_OS_IPHONE
|
||||
#if !TARGET_IPHONE_SIMULATOR
|
||||
#if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_TV
|
||||
const char* primaryInterface = "en0"; // WiFi interface on iOS
|
||||
#endif
|
||||
#else
|
||||
@@ -247,7 +255,7 @@ NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
if (store) {
|
||||
CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same
|
||||
if (info) {
|
||||
primaryInterface = [[NSString stringWithString:[(ARC_BRIDGE NSDictionary*)info objectForKey:@"PrimaryInterface"]] UTF8String];
|
||||
primaryInterface = [[NSString stringWithString:[(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"]] UTF8String];
|
||||
CFRelease(info);
|
||||
}
|
||||
CFRelease(store);
|
||||
@@ -259,8 +267,10 @@ NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
struct ifaddrs* list;
|
||||
if (getifaddrs(&list) >= 0) {
|
||||
for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) {
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV
|
||||
// Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator
|
||||
// Assumption holds for Apple TV running tvOS
|
||||
if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1"))
|
||||
#else
|
||||
if (strcmp(ifap->ifa_name, primaryInterface))
|
||||
#endif
|
||||
@@ -280,7 +290,7 @@ NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
const char* string = [ARC_AUTORELEASE([[NSString alloc] initWithFormat:format arguments:arguments]) UTF8String];
|
||||
const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String];
|
||||
va_end(arguments);
|
||||
unsigned char md5[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5(string, (CC_LONG)strlen(string), md5);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,35 +28,6 @@
|
||||
#import <os/object.h>
|
||||
#import <sys/socket.h>
|
||||
|
||||
/**
|
||||
* ARC <-> MRC compatibility macros.
|
||||
*/
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
#define ARC_BRIDGE __bridge
|
||||
#define ARC_BRIDGE_RELEASE(__OBJECT__) CFBridgingRelease(__OBJECT__)
|
||||
#define ARC_RETAIN(__OBJECT__) __OBJECT__
|
||||
#define ARC_RELEASE(__OBJECT__)
|
||||
#define ARC_AUTORELEASE(__OBJECT__) __OBJECT__
|
||||
#define ARC_DEALLOC(__OBJECT__)
|
||||
#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
#define ARC_DISPATCH_RETAIN(__OBJECT__)
|
||||
#define ARC_DISPATCH_RELEASE(__OBJECT__)
|
||||
#else
|
||||
#define ARC_DISPATCH_RETAIN(__OBJECT__) dispatch_retain(__OBJECT__)
|
||||
#define ARC_DISPATCH_RELEASE(__OBJECT__) dispatch_release(__OBJECT__)
|
||||
#endif
|
||||
#else
|
||||
#define ARC_BRIDGE
|
||||
#define ARC_BRIDGE_RELEASE(__OBJECT__) [(id)__OBJECT__ autorelease]
|
||||
#define ARC_RETAIN(__OBJECT__) [__OBJECT__ retain]
|
||||
#define ARC_RELEASE(__OBJECT__) [__OBJECT__ release]
|
||||
#define ARC_AUTORELEASE(__OBJECT__) [__OBJECT__ autorelease]
|
||||
#define ARC_DEALLOC(__OBJECT__) [__OBJECT__ dealloc]
|
||||
#define ARC_DISPATCH_RETAIN(__OBJECT__) dispatch_retain(__OBJECT__)
|
||||
#define ARC_DISPATCH_RELEASE(__OBJECT__) dispatch_release(__OBJECT__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* All GCDWebServer headers.
|
||||
*/
|
||||
@@ -77,15 +48,28 @@
|
||||
#import "GCDWebServerFileResponse.h"
|
||||
#import "GCDWebServerStreamedResponse.h"
|
||||
|
||||
/**
|
||||
* Check if a custom logging facility should be used instead.
|
||||
*/
|
||||
|
||||
#if defined(__GCDWEBSERVER_LOGGING_HEADER__)
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__
|
||||
|
||||
#import __GCDWEBSERVER_LOGGING_HEADER__
|
||||
|
||||
/**
|
||||
* Automatically detect if XLFacility is available and if so use it as a
|
||||
* logging facility.
|
||||
*/
|
||||
|
||||
#if defined(__has_include) && __has_include("XLFacilityMacros.h")
|
||||
#elif defined(__has_include) && __has_include("XLFacilityMacros.h")
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__
|
||||
|
||||
#undef XLOG_TAG
|
||||
#define XLOG_TAG @"gcdwebserver.internal"
|
||||
|
||||
#import "XLFacilityMacros.h"
|
||||
|
||||
#define GWS_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__)
|
||||
@@ -93,7 +77,6 @@
|
||||
#define GWS_LOG_INFO(...) XLOG_INFO(__VA_ARGS__)
|
||||
#define GWS_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__)
|
||||
#define GWS_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__)
|
||||
#define GWS_LOG_EXCEPTION(__EXCEPTION__) XLOG_EXCEPTION(__EXCEPTION__)
|
||||
|
||||
#define GWS_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__)
|
||||
#define GWS_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE()
|
||||
@@ -103,32 +86,21 @@
|
||||
* 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__
|
||||
|
||||
#undef LOG_LEVEL_DEF
|
||||
#define LOG_LEVEL_DEF GCDWebServerLogLevel
|
||||
extern int GCDWebServerLogLevel;
|
||||
extern DDLogLevel GCDWebServerLogLevel;
|
||||
|
||||
#define GWS_LOG_DEBUG(...) DDLogDebug(__VA_ARGS__)
|
||||
#define GWS_LOG_VERBOSE(...) DDLogVerbose(__VA_ARGS__)
|
||||
#define GWS_LOG_INFO(...) DDLogInfo(__VA_ARGS__)
|
||||
#define GWS_LOG_WARNING(...) DDLogWarn(__VA_ARGS__)
|
||||
#define GWS_LOG_ERROR(...) DDLogError(__VA_ARGS__)
|
||||
#define GWS_LOG_EXCEPTION(__EXCEPTION__) DDLogError(@"%@", __EXCEPTION__)
|
||||
|
||||
/**
|
||||
* Check if a custom logging facility should be used instead.
|
||||
*/
|
||||
|
||||
#elif defined(__GCDWEBSERVER_LOGGING_HEADER__)
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__
|
||||
|
||||
#import __GCDWEBSERVER_LOGGING_HEADER__
|
||||
|
||||
/**
|
||||
* If all of the above fail, then use GCDWebServer built-in
|
||||
@@ -144,8 +116,7 @@ typedef NS_ENUM(int, GCDWebServerLoggingLevel) {
|
||||
kGCDWebServerLoggingLevel_Verbose,
|
||||
kGCDWebServerLoggingLevel_Info,
|
||||
kGCDWebServerLoggingLevel_Warning,
|
||||
kGCDWebServerLoggingLevel_Error,
|
||||
kGCDWebServerLoggingLevel_Exception,
|
||||
kGCDWebServerLoggingLevel_Error
|
||||
};
|
||||
|
||||
extern GCDWebServerLoggingLevel GCDWebServerLogLevel;
|
||||
@@ -160,7 +131,6 @@ extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* for
|
||||
#define GWS_LOG_INFO(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); } while (0)
|
||||
#define GWS_LOG_WARNING(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); } while (0)
|
||||
#define GWS_LOG_ERROR(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); } while (0)
|
||||
#define GWS_LOG_EXCEPTION(__EXCEPTION__) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Exception) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Exception, @"%@", __EXCEPTION__); } while (0)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -194,7 +164,6 @@ extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* for
|
||||
*/
|
||||
|
||||
#define kGCDWebServerDefaultMimeType @"application/octet-stream"
|
||||
#define kGCDWebServerGCDQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||
#define kGCDWebServerErrorDomain @"GCDWebServerErrorDomain"
|
||||
|
||||
static inline BOOL GCDWebServerIsValidByteRange(NSRange range) {
|
||||
@@ -226,6 +195,7 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
|
||||
@property(nonatomic, readonly) NSDictionary* authenticationBasicAccounts;
|
||||
@property(nonatomic, readonly) NSDictionary* authenticationDigestAccounts;
|
||||
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
|
||||
@property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority;
|
||||
- (void)willStartConnection:(GCDWebServerConnection*)connection;
|
||||
- (void)didEndConnection:(GCDWebServerConnection*)connection;
|
||||
@end
|
||||
@@ -237,6 +207,8 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
|
||||
|
||||
@interface GCDWebServerRequest ()
|
||||
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
|
||||
@property(nonatomic, readwrite) NSData* localAddressData;
|
||||
@property(nonatomic, readwrite) NSData* remoteAddressData;
|
||||
- (void)prepareForWriting;
|
||||
- (BOOL)performOpen:(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.
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <zlib.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
@@ -84,11 +88,13 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
- (BOOL)open:(NSError**)error {
|
||||
int result = inflateInit2(&_stream, 15 + 16);
|
||||
if (result != Z_OK) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (![super open:error]) {
|
||||
deflateEnd(&_stream);
|
||||
inflateEnd(&_stream);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
@@ -110,8 +116,9 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
_stream.avail_out = (uInt)maxLength;
|
||||
int result = inflate(&_stream, Z_NO_FLUSH);
|
||||
if ((result != Z_OK) && (result != Z_STREAM_END)) {
|
||||
ARC_RELEASE(decodedData);
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
length += maxLength - _stream.avail_out;
|
||||
@@ -125,7 +132,6 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
}
|
||||
decodedData.length = length;
|
||||
BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet
|
||||
ARC_RELEASE(decodedData);
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -151,6 +157,8 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
NSString* _noneMatch;
|
||||
NSRange _range;
|
||||
BOOL _gzipAccepted;
|
||||
NSData* _localAddress;
|
||||
NSData* _remoteAddress;
|
||||
|
||||
BOOL _opened;
|
||||
NSMutableArray* _decoders;
|
||||
@@ -162,24 +170,24 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
@implementation GCDWebServerRequest : NSObject
|
||||
|
||||
@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 {
|
||||
if ((self = [super init])) {
|
||||
_method = [method copy];
|
||||
_url = ARC_RETAIN(url);
|
||||
_headers = ARC_RETAIN(headers);
|
||||
_url = url;
|
||||
_headers = headers;
|
||||
_path = [path copy];
|
||||
_query = ARC_RETAIN(query);
|
||||
_query = query;
|
||||
|
||||
_type = ARC_RETAIN(GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]));
|
||||
_type = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]);
|
||||
_chunked = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"];
|
||||
NSString* lengthHeader = [_headers objectForKey:@"Content-Length"];
|
||||
if (lengthHeader) {
|
||||
NSInteger length = [lengthHeader integerValue];
|
||||
if (_chunked || (length < 0)) {
|
||||
GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _url);
|
||||
GWS_DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
_length = length;
|
||||
@@ -193,9 +201,8 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
_length = NSUIntegerMax;
|
||||
} else {
|
||||
if (_type) {
|
||||
GWS_DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _url);
|
||||
_type = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense
|
||||
}
|
||||
_length = NSUIntegerMax;
|
||||
}
|
||||
@@ -204,7 +211,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
if (modifiedHeader) {
|
||||
_modifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy];
|
||||
}
|
||||
_noneMatch = ARC_RETAIN([_headers objectForKey:@"If-None-Match"]);
|
||||
_noneMatch = [_headers objectForKey:@"If-None-Match"];
|
||||
|
||||
_range = NSMakeRange(NSUIntegerMax, 0);
|
||||
NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]);
|
||||
@@ -246,21 +253,6 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_method);
|
||||
ARC_RELEASE(_url);
|
||||
ARC_RELEASE(_headers);
|
||||
ARC_RELEASE(_path);
|
||||
ARC_RELEASE(_query);
|
||||
ARC_RELEASE(_type);
|
||||
ARC_RELEASE(_modifiedSince);
|
||||
ARC_RELEASE(_noneMatch);
|
||||
ARC_RELEASE(_decoders);
|
||||
ARC_RELEASE(_attributes);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)hasBody {
|
||||
return _type ? YES : NO;
|
||||
}
|
||||
@@ -290,7 +282,6 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
if ([GCDWebServerNormalizeHeaderValue([self.headers objectForKey:@"Content-Encoding"]) isEqualToString:@"gzip"]) {
|
||||
GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer];
|
||||
[_decoders addObject:decoder];
|
||||
ARC_RELEASE(decoder);
|
||||
_writer = decoder;
|
||||
}
|
||||
}
|
||||
@@ -320,6 +311,14 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
[_attributes setValue:attribute forKey:key];
|
||||
}
|
||||
|
||||
- (NSString*)localAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_localAddress.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)remoteAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_remoteAddress.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path];
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <zlib.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
@@ -90,7 +94,9 @@
|
||||
- (BOOL)open:(NSError**)error {
|
||||
int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
if (result != Z_OK) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (![super open:error]) {
|
||||
@@ -126,8 +132,9 @@
|
||||
if (result == Z_STREAM_END) {
|
||||
_finished = YES;
|
||||
} else if (result != Z_OK) {
|
||||
ARC_RELEASE(encodedData);
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
length += maxLength - _stream.avail_out;
|
||||
@@ -140,7 +147,7 @@
|
||||
} while (length == 0); // Make sure we don't return an empty NSData if not in finished state
|
||||
encodedData.length = length;
|
||||
}
|
||||
return ARC_AUTORELEASE(encodedData);
|
||||
return encodedData;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
@@ -174,7 +181,7 @@
|
||||
gzipContentEncodingEnabled=_gzipped, additionalHeaders=_headers;
|
||||
|
||||
+ (instancetype)response {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] init]);
|
||||
return [[[self class] alloc] init];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
@@ -189,16 +196,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_type);
|
||||
ARC_RELEASE(_lastModified);
|
||||
ARC_RELEASE(_eTag);
|
||||
ARC_RELEASE(_headers);
|
||||
ARC_RELEASE(_encoders);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header {
|
||||
[_headers setValue:value forKey:header];
|
||||
}
|
||||
@@ -228,7 +225,6 @@
|
||||
if (_gzipped) {
|
||||
GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader];
|
||||
[_encoders addObject:encoder];
|
||||
ARC_RELEASE(encoder);
|
||||
_reader = encoder;
|
||||
}
|
||||
}
|
||||
@@ -245,8 +241,9 @@
|
||||
}
|
||||
|
||||
- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
|
||||
GWS_DCHECK(_opened);
|
||||
if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) {
|
||||
[_reader asyncReadDataWithCompletion:block];
|
||||
[_reader asyncReadDataWithCompletion:[block copy]];
|
||||
} else {
|
||||
NSError* error = nil;
|
||||
NSData* data = [_reader readData:&error];
|
||||
@@ -288,11 +285,11 @@
|
||||
@implementation GCDWebServerResponse (Extensions)
|
||||
|
||||
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithStatusCode:statusCode]);
|
||||
return [[self alloc] initWithStatusCode:statusCode];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithRedirect:location permanent:permanent]);
|
||||
return [[self alloc] initWithRedirect:location permanent:permanent];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStatusCode:(NSInteger)statusCode {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerDataRequest () {
|
||||
@@ -40,14 +44,6 @@
|
||||
|
||||
@synthesize data=_data;
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_data);
|
||||
ARC_RELEASE(_text);
|
||||
ARC_RELEASE(_jsonObject);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
if (self.contentLength != NSUIntegerMax) {
|
||||
_data = [[NSMutableData alloc] initWithCapacity:self.contentLength];
|
||||
@@ -55,7 +51,9 @@
|
||||
_data = [[NSMutableData alloc] init];
|
||||
}
|
||||
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 YES;
|
||||
@@ -99,7 +97,7 @@
|
||||
if (_jsonObject == nil) {
|
||||
NSString* mimeType = GCDWebServerTruncateHeaderValue(self.contentType);
|
||||
if ([mimeType isEqualToString:@"application/json"] || [mimeType isEqualToString:@"text/json"] || [mimeType isEqualToString:@"text/javascript"]) {
|
||||
_jsonObject = ARC_RETAIN([NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL]);
|
||||
_jsonObject = [NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerFileRequest () {
|
||||
@@ -40,22 +44,21 @@
|
||||
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
||||
_temporaryPath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
|
||||
_temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
unlink([_temporaryPath fileSystemRepresentation]);
|
||||
ARC_RELEASE(_temporaryPath);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
_file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (_file <= 0) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
@@ -63,7 +66,9 @@
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
if (write(_file, data.bytes, data.length) != (ssize_t)data.length) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
@@ -71,7 +76,9 @@
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
if (close(_file) < 0) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
#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.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kMultiPartBufferSize (256 * 1024)
|
||||
@@ -63,19 +67,11 @@ static NSData* _dashNewlineData = nil;
|
||||
if ((self = [super init])) {
|
||||
_controlName = [name copy];
|
||||
_contentType = [type copy];
|
||||
_mimeType = ARC_RETAIN(GCDWebServerTruncateHeaderValue(_contentType));
|
||||
_mimeType = GCDWebServerTruncateHeaderValue(_contentType);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_controlName);
|
||||
ARC_RELEASE(_contentType);
|
||||
ARC_RELEASE(_mimeType);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartArgument () {
|
||||
@@ -91,7 +87,7 @@ static NSData* _dashNewlineData = nil;
|
||||
|
||||
- (id)initWithControlName:(NSString*)name contentType:(NSString*)type data:(NSData*)data {
|
||||
if ((self = [super initWithControlName:name contentType:type])) {
|
||||
_data = ARC_RETAIN(data);
|
||||
_data = data;
|
||||
|
||||
if ([self.contentType hasPrefix:@"text/"]) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||
@@ -101,13 +97,6 @@ static NSData* _dashNewlineData = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_data);
|
||||
ARC_RELEASE(_string);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | %lu bytes>", [self class], self.mimeType, (unsigned long)_data.length];
|
||||
}
|
||||
@@ -135,11 +124,6 @@ static NSData* _dashNewlineData = nil;
|
||||
|
||||
- (void)dealloc {
|
||||
unlink([_temporaryPath fileSystemRepresentation]);
|
||||
|
||||
ARC_RELEASE(_fileName);
|
||||
ARC_RELEASE(_temporaryPath);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
@@ -187,14 +171,13 @@ static NSData* _dashNewlineData = nil;
|
||||
NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil;
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
if ((self = [super init])) {
|
||||
_boundary = ARC_RETAIN(data);
|
||||
_defaultcontrolName = ARC_RETAIN(name);
|
||||
_arguments = ARC_RETAIN(arguments);
|
||||
_files = ARC_RETAIN(files);
|
||||
_boundary = data;
|
||||
_defaultcontrolName = name;
|
||||
_arguments = arguments;
|
||||
_files = files;
|
||||
_data = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize];
|
||||
_state = kParserState_Start;
|
||||
}
|
||||
@@ -202,23 +185,10 @@ static NSData* _dashNewlineData = nil;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_boundary);
|
||||
ARC_RELEASE(_defaultcontrolName);
|
||||
ARC_RELEASE(_data);
|
||||
ARC_RELEASE(_arguments);
|
||||
ARC_RELEASE(_files);
|
||||
|
||||
ARC_RELEASE(_controlName);
|
||||
ARC_RELEASE(_fileName);
|
||||
ARC_RELEASE(_contentType);
|
||||
if (_tmpFile > 0) {
|
||||
close(_tmpFile);
|
||||
unlink([_tmpPath fileSystemRepresentation]);
|
||||
}
|
||||
ARC_RELEASE(_tmpPath);
|
||||
ARC_RELEASE(_subParser);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
|
||||
@@ -229,15 +199,10 @@ static NSData* _dashNewlineData = nil;
|
||||
NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
|
||||
ARC_RELEASE(_controlName);
|
||||
_controlName = nil;
|
||||
ARC_RELEASE(_fileName);
|
||||
_fileName = nil;
|
||||
ARC_RELEASE(_contentType);
|
||||
_contentType = nil;
|
||||
ARC_RELEASE(_tmpPath);
|
||||
_tmpPath = nil;
|
||||
ARC_RELEASE(_subParser);
|
||||
_subParser = nil;
|
||||
NSString* headers = [[NSString alloc] initWithData:[_data subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding];
|
||||
if (headers) {
|
||||
@@ -247,15 +212,15 @@ static NSData* _dashNewlineData = nil;
|
||||
NSString* name = [header substringToIndex:subRange.location];
|
||||
NSString* value = [[header substringFromIndex:(subRange.location + subRange.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
if ([name caseInsensitiveCompare:@"Content-Type"] == NSOrderedSame) {
|
||||
_contentType = ARC_RETAIN(GCDWebServerNormalizeHeaderValue(value));
|
||||
_contentType = GCDWebServerNormalizeHeaderValue(value);
|
||||
} else if ([name caseInsensitiveCompare:@"Content-Disposition"] == NSOrderedSame) {
|
||||
NSString* contentDisposition = GCDWebServerNormalizeHeaderValue(value);
|
||||
if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"form-data"]) {
|
||||
_controlName = ARC_RETAIN(GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name"));
|
||||
_fileName = ARC_RETAIN(GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"));
|
||||
_controlName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name");
|
||||
_fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename");
|
||||
} else if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"file"]) {
|
||||
_controlName = ARC_RETAIN(_defaultcontrolName);
|
||||
_fileName = ARC_RETAIN(GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"));
|
||||
_controlName = _defaultcontrolName;
|
||||
_fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -265,7 +230,6 @@ static NSData* _dashNewlineData = nil;
|
||||
if (_contentType == nil) {
|
||||
_contentType = @"text/plain";
|
||||
}
|
||||
ARC_RELEASE(headers);
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed decoding headers in part of 'multipart/form-data'");
|
||||
GWS_DNOT_REACHED();
|
||||
@@ -314,7 +278,6 @@ static NSData* _dashNewlineData = nil;
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
ARC_RELEASE(_subParser);
|
||||
_subParser = nil;
|
||||
} else if (_tmpPath) {
|
||||
ssize_t result = write(_tmpFile, dataBytes, dataLength);
|
||||
@@ -323,7 +286,6 @@ static NSData* _dashNewlineData = nil;
|
||||
_tmpFile = 0;
|
||||
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithControlName:_controlName contentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
|
||||
[_files addObject:file];
|
||||
ARC_RELEASE(file);
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
@@ -332,14 +294,11 @@ static NSData* _dashNewlineData = nil;
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
ARC_RELEASE(_tmpPath);
|
||||
_tmpPath = nil;
|
||||
} else {
|
||||
NSData* data = [[NSData alloc] initWithBytes:(void*)dataBytes length:dataLength];
|
||||
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithControlName:_controlName contentType:_contentType data:data];
|
||||
[_arguments addObject:argument];
|
||||
ARC_RELEASE(argument);
|
||||
ARC_RELEASE(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,18 +372,13 @@ static NSData* _dashNewlineData = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_arguments);
|
||||
ARC_RELEASE(_files);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary");
|
||||
_parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files];
|
||||
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 YES;
|
||||
@@ -432,7 +386,9 @@ static NSData* _dashNewlineData = nil;
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
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 YES;
|
||||
@@ -440,10 +396,11 @@ static NSData* _dashNewlineData = nil;
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
BOOL atEnd = [_parser isAtEnd];
|
||||
ARC_RELEASE(_parser);
|
||||
_parser = nil;
|
||||
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 YES;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerURLEncodedFormRequest () {
|
||||
@@ -41,12 +45,6 @@
|
||||
return @"application/x-www-form-urlencoded";
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_arguments);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
if (![super close:error]) {
|
||||
return NO;
|
||||
@@ -54,9 +52,8 @@
|
||||
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
_arguments = ARC_RETAIN(GCDWebServerParseURLEncodedForm(string));
|
||||
_arguments = GCDWebServerParseURLEncodedForm(string);
|
||||
GWS_DCHECK(_arguments);
|
||||
ARC_RELEASE(string);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerDataResponse () {
|
||||
@@ -37,18 +41,17 @@
|
||||
@implementation GCDWebServerDataResponse
|
||||
|
||||
+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithData:data contentType:type]);
|
||||
return [[[self class] alloc] initWithData:data contentType:type];
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type {
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
_data = ARC_RETAIN(data);
|
||||
_data = data;
|
||||
|
||||
self.contentType = type;
|
||||
self.contentLength = data.length;
|
||||
@@ -56,12 +59,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_data);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
NSData* data;
|
||||
if (_done) {
|
||||
@@ -85,30 +82,29 @@
|
||||
@implementation GCDWebServerDataResponse (Extensions)
|
||||
|
||||
+ (instancetype)responseWithText:(NSString*)text {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithText:text]);
|
||||
return [[self alloc] initWithText:text];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithHTML:(NSString*)html {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithHTML:html]);
|
||||
return [[self alloc] initWithHTML:html];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithHTMLTemplate:path variables:variables]);
|
||||
return [[self alloc] initWithHTMLTemplate:path variables:variables];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithJSONObject:(id)object {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithJSONObject:object]);
|
||||
return [[self alloc] initWithJSONObject:object];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithJSONObject:object contentType:type]);
|
||||
return [[self alloc] initWithJSONObject:object contentType:type];
|
||||
}
|
||||
|
||||
- (instancetype)initWithText:(NSString*)text {
|
||||
NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/plain; charset=utf-8"];
|
||||
@@ -118,7 +114,6 @@
|
||||
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/html; charset=utf-8"];
|
||||
@@ -130,7 +125,6 @@
|
||||
[html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)];
|
||||
}];
|
||||
id response = [self initWithHTML:html];
|
||||
ARC_RELEASE(html);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -141,7 +135,6 @@
|
||||
- (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL];
|
||||
if (data == nil) {
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:type];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerErrorResponse ()
|
||||
@@ -37,7 +41,7 @@
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = ARC_AUTORELEASE([[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]);
|
||||
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
@@ -46,7 +50,7 @@
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = ARC_AUTORELEASE([[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]);
|
||||
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
@@ -55,7 +59,7 @@
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = ARC_AUTORELEASE([[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]);
|
||||
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
@@ -64,7 +68,7 @@
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = ARC_AUTORELEASE([[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]);
|
||||
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
@@ -82,7 +86,6 @@ static inline NSString* _EscapeHTMLString(NSString* string) {
|
||||
if ((self = [self initWithHTML:html])) {
|
||||
self.statusCode = statusCode;
|
||||
}
|
||||
ARC_RELEASE(message);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <sys/stat.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
@@ -43,19 +47,19 @@
|
||||
@implementation GCDWebServerFileResponse
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path]);
|
||||
return [[[self class] alloc] initWithFile:path];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path isAttachment:attachment]);
|
||||
return [[[self class] alloc] initWithFile:path isAttachment:attachment];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path byteRange:range]);
|
||||
return [[[self class] alloc] initWithFile:path byteRange:range];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment]);
|
||||
return [[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path {
|
||||
@@ -78,13 +82,11 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
||||
struct stat info;
|
||||
if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) {
|
||||
GWS_DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
#ifndef __LP64__
|
||||
if (info.st_size >= (off_t)4294967295) { // In 32 bit mode, we can't handle files greater than 4 GiBs (don't use "NSUIntegerMax" here to avoid potential unsigned to signed conversion issues)
|
||||
GWS_DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
@@ -100,7 +102,6 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
||||
range.location = fileSize - range.length;
|
||||
}
|
||||
if (range.length == 0) {
|
||||
ARC_RELEASE(self);
|
||||
return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header
|
||||
}
|
||||
} else {
|
||||
@@ -125,7 +126,6 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
||||
if (lossyFileName) {
|
||||
NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)];
|
||||
[self setValue:value forAdditionalHeader:@"Content-Disposition"];
|
||||
ARC_RELEASE(lossyFileName);
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
@@ -139,20 +139,18 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_path);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
||||
if (_file <= 0) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
close(_file);
|
||||
return NO;
|
||||
}
|
||||
@@ -164,14 +162,16 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithLength:length];
|
||||
ssize_t result = read(_file, data.mutableBytes, length);
|
||||
if (result < 0) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
if (result > 0) {
|
||||
[data setLength:result];
|
||||
_size -= result;
|
||||
}
|
||||
return ARC_AUTORELEASE(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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 block must return empty NSData when done or nil on error and set the
|
||||
* "error" argument which is guaranteed to be non-NULL.
|
||||
* The block must return either a chunk of data, an empty NSData when done, or
|
||||
* nil on error and set the "error" argument which is guaranteed to be non-NULL.
|
||||
*/
|
||||
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
|
||||
* truly asynchronous generation of the data.
|
||||
*
|
||||
* The block must return empty NSData when done or nil on error and set the
|
||||
* "error" argument which is guaranteed to be non-NULL.
|
||||
* The block must call "completionBlock" passing the new chunk of data when ready,
|
||||
* an empty NSData when done, or nil on error and pass a NSError.
|
||||
*
|
||||
* The block must regularly call "completionBlock" passing new streamed data.
|
||||
* 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.
|
||||
* The block cannot call "completionBlock" more than once per invocation.
|
||||
*/
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerStreamedResponse () {
|
||||
@@ -36,11 +40,11 @@
|
||||
@implementation GCDWebServerStreamedResponse
|
||||
|
||||
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type streamBlock:block]);
|
||||
return [[[self class] alloc] initWithContentType:type streamBlock:block];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type asyncStreamBlock:block]);
|
||||
return [[[self class] alloc] initWithContentType:type asyncStreamBlock:block];
|
||||
}
|
||||
|
||||
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
@@ -62,12 +66,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_block);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
|
||||
_block(block);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -65,7 +65,7 @@
|
||||
<div id="alerts"></div>
|
||||
|
||||
<div class="btn-toolbar">
|
||||
<button type="button" class="btn btn-primary fileinput-button">
|
||||
<button type="button" class="btn btn-primary fileinput-button" id="upload-file">
|
||||
<span class="glyphicon glyphicon-upload"></span> Upload Files…
|
||||
<input id="fileupload" type="file" name="files[]" multiple>
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -178,6 +178,17 @@ function _reload(path) {
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// Workaround Firefox and IE not showing file selection dialog when clicking on "upload-file" <button>
|
||||
// Making it a <div> instead also works but then it the button doesn't work anymore with tab selection or accessibility
|
||||
$("#upload-file").click(function(event) {
|
||||
$("#fileupload").click();
|
||||
});
|
||||
|
||||
// Prevent event bubbling when using workaround above
|
||||
$("#fileupload").click(function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
$("#fileupload").fileupload({
|
||||
dropZone: $(document),
|
||||
pasteZone: null,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -25,6 +25,10 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebUploader requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
@@ -288,21 +292,16 @@
|
||||
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden,
|
||||
title=_title, header=_header, prologue=_prologue, epilogue=_epilogue, footer=_footer;
|
||||
|
||||
@dynamic delegate;
|
||||
|
||||
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
||||
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 !__has_feature(objc_arc)
|
||||
[self release];
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
||||
#if __has_feature(objc_arc)
|
||||
GCDWebUploader* __unsafe_unretained server = self;
|
||||
#else
|
||||
__block GCDWebUploader* server = self;
|
||||
#endif
|
||||
|
||||
// Resource files
|
||||
[self addGETHandlerForBasePath:@"/" directoryPath:[siteBundle resourcePath] indexFilename:nil cacheAge:3600 allowRangeRequests:NO];
|
||||
@@ -313,11 +312,7 @@
|
||||
#if TARGET_OS_IPHONE
|
||||
NSString* device = [[UIDevice currentDevice] name];
|
||||
#else
|
||||
#if __has_feature(objc_arc)
|
||||
NSString* device = CFBridgingRelease(SCDynamicStoreCopyComputerName(NULL, NULL));
|
||||
#else
|
||||
NSString* device = [(id)SCDynamicStoreCopyComputerName(NULL, NULL) autorelease];
|
||||
#endif
|
||||
#endif
|
||||
NSString* title = server.title;
|
||||
if (title == nil) {
|
||||
@@ -401,22 +396,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
|
||||
- (void)dealloc {
|
||||
[_uploadDirectory release];
|
||||
[_allowedExtensions release];
|
||||
[_title release];
|
||||
[_header release];
|
||||
[_prologue release];
|
||||
[_epilogue release];
|
||||
[_footer release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebUploader (Subclassing)
|
||||
|
||||
50
Mac/main.m
50
Mac/main.m
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -72,6 +72,10 @@ typedef enum {
|
||||
[self _logDelegateCall:_cmd];
|
||||
}
|
||||
|
||||
- (void)webServerDidUpdateNATPortMapping:(GCDWebServer*)server {
|
||||
[self _logDelegateCall:_cmd];
|
||||
}
|
||||
|
||||
- (void)webServerDidConnect:(GCDWebServer*)server {
|
||||
[self _logDelegateCall:_cmd];
|
||||
}
|
||||
@@ -141,9 +145,11 @@ int main(int argc, const char* argv[]) {
|
||||
NSString* authenticationRealm = nil;
|
||||
NSString* authenticationUser = nil;
|
||||
NSString* authenticationPassword = nil;
|
||||
BOOL bindToLocalhost = NO;
|
||||
BOOL requestNATPortMapping = NO;
|
||||
|
||||
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]\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]));
|
||||
} else {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (argv[i][0] != '-') {
|
||||
@@ -188,6 +194,10 @@ int main(int argc, const char* argv[]) {
|
||||
} else if (!strcmp(argv[i], "-authenticationPassword") && (i + 1 < argc)) {
|
||||
++i;
|
||||
authenticationPassword = [NSString stringWithUTF8String:argv[i]];
|
||||
} else if (!strcmp(argv[i], "--localhost")) {
|
||||
bindToLocalhost = YES;
|
||||
} else if (!strcmp(argv[i], "--nat")) {
|
||||
requestNATPortMapping = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,7 +364,7 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stdout, "Running in Async Response mode");
|
||||
webServer = [[GCDWebServer alloc] init];
|
||||
[webServer addHandlerForMethod:@"GET"
|
||||
path:@"/"
|
||||
path:@"/async"
|
||||
requestClass:[GCDWebServerRequest class]
|
||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
|
||||
@@ -363,16 +373,34 @@ int main(int argc, const char* argv[]) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
#if __has_feature(objc_arc)
|
||||
fprintf(stdout, " (ARC is ON)\n");
|
||||
#else
|
||||
fprintf(stdout, " (ARC is OFF)\n");
|
||||
#endif
|
||||
|
||||
if (webServer) {
|
||||
Delegate* delegate = [[Delegate alloc] init];
|
||||
@@ -391,6 +419,8 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stdout, "\n");
|
||||
NSMutableDictionary* options = [NSMutableDictionary dictionary];
|
||||
[options setObject:@8080 forKey:GCDWebServerOption_Port];
|
||||
[options setObject:@(requestNATPortMapping) forKey:GCDWebServerOption_RequestNATPortMapping];
|
||||
[options setObject:@(bindToLocalhost) forKey:GCDWebServerOption_BindToLocalhost];
|
||||
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
||||
if (authenticationUser && authenticationPassword) {
|
||||
[options setValue:authenticationRealm forKey:GCDWebServerOption_AuthenticationRealm];
|
||||
@@ -406,10 +436,6 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
webServer.delegate = nil;
|
||||
#if !__has_feature(objc_arc)
|
||||
[delegate release];
|
||||
[webServer release];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
169
README.md
169
README.md
@@ -6,8 +6,6 @@ Overview
|
||||
[](https://github.com/swisspol/GCDWebServer)
|
||||
[](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:
|
||||
* 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
|
||||
@@ -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
|
||||
* Automatically handle transitions between foreground, background and suspended modes in iOS apps
|
||||
* Full support for both IPv4 and IPv6
|
||||
* NAT port mapping (IPv4 only)
|
||||
|
||||
Included extensions:
|
||||
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of ```GCDWebServer``` that implements an interface for uploading and downloading files using a web browser
|
||||
@@ -38,13 +37,16 @@ What's not supported (but not really required from an embedded HTTP server):
|
||||
Requirements:
|
||||
* OS X 10.7 or later (x86_64)
|
||||
* iOS 5.0 or later (armv7, armv7s or arm64)
|
||||
* ARC memory management only (if you need MRC support use GCDWebServer 3.1 and earlier)
|
||||
|
||||
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.
|
||||
|
||||
Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Xcode project's Podfile:
|
||||
If you add the files directly then (1) link to `libz` (via Target > Build Phases > Link Binary With Libraries) and (2) add `$(SDKROOT)/usr/include/libxml2` to your header search paths (via Target > Build Settings > HEADER_SEARCH_PATHS).
|
||||
|
||||
Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Podfile:
|
||||
```
|
||||
pod "GCDWebServer", "~> 3.0"
|
||||
```
|
||||
@@ -57,11 +59,29 @@ Or this line for GCDWebDAVServer:
|
||||
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!
|
||||
|
||||
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.
|
||||
|
||||
**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):**
|
||||
```objectivec
|
||||
#import "GCDWebServer.h"
|
||||
@@ -133,83 +153,29 @@ int main(int argc, const char* argv[]) {
|
||||
***webServer.swift***
|
||||
```swift
|
||||
import Foundation
|
||||
import GCDWebServer
|
||||
|
||||
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>")
|
||||
|
||||
})
|
||||
|
||||
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***
|
||||
```objectivec
|
||||
#import "GCDWebServer.h"
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import <GCDWebServer/GCDWebServer.h>
|
||||
#import <GCDWebServer/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]
|
||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
|
||||
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
|
||||
=============================
|
||||
|
||||
@@ -273,6 +239,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):
|
||||
|
||||
**OS X version (command line tool):**
|
||||
```objectivec
|
||||
#import "GCDWebServer.h"
|
||||
|
||||
@@ -309,7 +276,7 @@ GCDWebServer's architecture consists of only 4 core classes:
|
||||
Implementing Handlers
|
||||
=====================
|
||||
|
||||
GCDWebServer relies on "handlers" to process incoming web requests and generating responses. Handlers are implemented with GCD blocks which makes it very easy to provide your owns. However, they are executed on arbitrary threads within GCD so __special attention must be paid to thread-safety and re-entrancy__.
|
||||
GCDWebServer relies on "handlers" to process incoming web requests and generating responses. Handlers are implemented with GCD blocks which makes it very easy to provide your own. However, they are executed on arbitrary threads within GCD so __special attention must be paid to thread-safety and re-entrancy__.
|
||||
|
||||
Handlers require 2 GCD blocks:
|
||||
* The ```GCDWebServerMatchBlock``` is called on every handler added to the ```GCDWebServer``` instance whenever a web request has started (i.e. HTTP headers have been received). It is passed the basic info for the web request (HTTP method, URL, headers...) and must decide if it wants to handle it or not. If yes, it must return a new ```GCDWebServerRequest``` instance (see above) created with this info. Otherwise, it simply returns nil.
|
||||
@@ -317,6 +284,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.
|
||||
|
||||
Asynchronous HTTP Responses
|
||||
===========================
|
||||
|
||||
New in GCDWebServer 3.0 is the ability to process HTTP requests asynchronously 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
|
||||
===========================================
|
||||
|
||||
|
||||
100
Run-Tests.sh
100
Run-Tests.sh
@@ -1,23 +1,25 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
OSX_SDK="macosx"
|
||||
if [ -z "$TRAVIS" ]; then
|
||||
IOS_SDK="iphoneos"
|
||||
else
|
||||
IOS_SDK="iphonesimulator"
|
||||
fi
|
||||
IOS_SDK="iphonesimulator"
|
||||
TVOS_SDK="appletvsimulator"
|
||||
|
||||
OSX_SDK_VERSION=`xcodebuild -version -sdk | grep -A 1 '^MacOSX' | tail -n 1 | awk '{ print $2 }'`
|
||||
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)"
|
||||
IOS_TARGET="GCDWebServer (iOS)"
|
||||
TVOS_TARGET="GCDWebServer (tvOS)"
|
||||
CONFIGURATION="Release"
|
||||
|
||||
MRC_BUILD_DIR="/tmp/GCDWebServer-MRC"
|
||||
MRC_PRODUCT="$MRC_BUILD_DIR/$CONFIGURATION/GCDWebServer"
|
||||
ARC_BUILD_DIR="/tmp/GCDWebServer-ARC"
|
||||
ARC_PRODUCT="$ARC_BUILD_DIR/$CONFIGURATION/GCDWebServer"
|
||||
OSX_TEST_SCHEME="GCDWebServers (Mac)"
|
||||
|
||||
BUILD_DIR="/tmp/GCDWebServer-Build"
|
||||
PRODUCT="$BUILD_DIR/$CONFIGURATION/GCDWebServer"
|
||||
|
||||
PAYLOAD_ZIP="Tests/Payload.zip"
|
||||
PAYLOAD_DIR="/tmp/GCDWebServer"
|
||||
PAYLOAD_DIR="/tmp/GCDWebServer-Payload"
|
||||
|
||||
function runTests {
|
||||
rm -rf "$PAYLOAD_DIR"
|
||||
@@ -26,61 +28,45 @@ function runTests {
|
||||
if [ "$4" != "" ]; then
|
||||
cp -f "$4" "$PAYLOAD_DIR/Payload"
|
||||
pushd "$PAYLOAD_DIR/Payload"
|
||||
SetFile -d "1/1/2014 00:00:00" -m "1/1/2014 00:00:00" `basename "$4"`
|
||||
TZ=GMT SetFile -d "1/1/2014 00:00:00" -m "1/1/2014 00:00:00" `basename "$4"`
|
||||
popd
|
||||
fi
|
||||
logLevel=2 $1 -mode "$2" -root "$PAYLOAD_DIR/Payload" -tests "$3"
|
||||
}
|
||||
|
||||
# Build for iOS in manual memory management mode and for oldest deployment target (TODO: run tests on iOS)
|
||||
rm -rf "$MRC_BUILD_DIR"
|
||||
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" "IPHONEOS_DEPLOYMENT_TARGET=5.1.1" > /dev/null
|
||||
# Run built-in OS X tests
|
||||
rm -rf "$BUILD_DIR"
|
||||
xcodebuild test -scheme "$OSX_TEST_SCHEME" "SYMROOT=$BUILD_DIR"
|
||||
|
||||
# Build for iOS in manual memory management mode and for default deployment target (TODO: run tests on iOS)
|
||||
rm -rf "$MRC_BUILD_DIR"
|
||||
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
|
||||
|
||||
# Build for iOS in ARC mode and for oldest deployment target (TODO: run tests on iOS)
|
||||
rm -rf "$ARC_BUILD_DIR"
|
||||
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" "IPHONEOS_DEPLOYMENT_TARGET=5.1.1" > /dev/null
|
||||
|
||||
# Build for iOS in ARC mode and for default deployment target (TODO: run tests on iOS)
|
||||
rm -rf "$ARC_BUILD_DIR"
|
||||
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
|
||||
|
||||
# Build for OS X in manual memory management mode and for oldest deployment target
|
||||
rm -rf "$MRC_BUILD_DIR"
|
||||
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" "MACOSX_DEPLOYMENT_TARGET=10.7" > /dev/null
|
||||
|
||||
# Build for OS X in manual memory management mode and for default deployment target
|
||||
rm -rf "$MRC_BUILD_DIR"
|
||||
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
|
||||
|
||||
# Build for OS X in ARC mode and for oldest deployment target
|
||||
rm -rf "$ARC_BUILD_DIR"
|
||||
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" "MACOSX_DEPLOYMENT_TARGET=10.7" > /dev/null
|
||||
|
||||
# Build for OS X in ARC mode and for default deployment target
|
||||
rm -rf "$ARC_BUILD_DIR"
|
||||
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
|
||||
# Build for OS X for oldest supported deployment target
|
||||
rm -rf "$BUILD_DIR"
|
||||
xcodebuild build -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=10.7" > /dev/null
|
||||
|
||||
# Run tests
|
||||
runTests $MRC_PRODUCT "htmlForm" "Tests/HTMLForm"
|
||||
runTests $ARC_PRODUCT "htmlForm" "Tests/HTMLForm"
|
||||
runTests $MRC_PRODUCT "htmlFileUpload" "Tests/HTMLFileUpload"
|
||||
runTests $ARC_PRODUCT "htmlFileUpload" "Tests/HTMLFileUpload"
|
||||
runTests $MRC_PRODUCT "webServer" "Tests/WebServer"
|
||||
runTests $ARC_PRODUCT "webServer" "Tests/WebServer"
|
||||
runTests $MRC_PRODUCT "webDAV" "Tests/WebDAV-Transmit"
|
||||
runTests $ARC_PRODUCT "webDAV" "Tests/WebDAV-Transmit"
|
||||
runTests $MRC_PRODUCT "webDAV" "Tests/WebDAV-Cyberduck"
|
||||
runTests $ARC_PRODUCT "webDAV" "Tests/WebDAV-Cyberduck"
|
||||
runTests $MRC_PRODUCT "webDAV" "Tests/WebDAV-Finder"
|
||||
runTests $ARC_PRODUCT "webDAV" "Tests/WebDAV-Finder"
|
||||
runTests $MRC_PRODUCT "webUploader" "Tests/WebUploader"
|
||||
runTests $ARC_PRODUCT "webUploader" "Tests/WebUploader"
|
||||
runTests $MRC_PRODUCT "webServer" "Tests/WebServer-Sample-Movie" "Tests/Sample-Movie.mp4"
|
||||
runTests $ARC_PRODUCT "webServer" "Tests/WebServer-Sample-Movie" "Tests/Sample-Movie.mp4"
|
||||
runTests $PRODUCT "htmlForm" "Tests/HTMLForm"
|
||||
runTests $PRODUCT "htmlFileUpload" "Tests/HTMLFileUpload"
|
||||
runTests $PRODUCT "webServer" "Tests/WebServer"
|
||||
runTests $PRODUCT "webDAV" "Tests/WebDAV-Transmit"
|
||||
runTests $PRODUCT "webDAV" "Tests/WebDAV-Cyberduck"
|
||||
runTests $PRODUCT "webDAV" "Tests/WebDAV-Finder"
|
||||
runTests $PRODUCT "webUploader" "Tests/WebUploader"
|
||||
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
|
||||
echo "\nAll tests completed successfully!"
|
||||
|
||||
Binary file not shown.
BIN
Tests/WebDAV-Finder/059-206.response
Executable file
BIN
Tests/WebDAV-Finder/059-206.response
Executable file
Binary file not shown.
Binary file not shown.
BIN
Tests/WebDAV-Finder/062-206.response
Normal file
BIN
Tests/WebDAV-Finder/062-206.response
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,5 +28,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
@property(retain, nonatomic) UIWindow* window;
|
||||
@property(strong, nonatomic) UIWindow* window;
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -26,55 +26,11 @@
|
||||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "GCDWebUploader.h"
|
||||
|
||||
@interface AppDelegate () <GCDWebUploaderDelegate> {
|
||||
@private
|
||||
GCDWebUploader* _webServer;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
|
||||
- (void)dealloc {
|
||||
[_window release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
- (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
|
||||
|
||||
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>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<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>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<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.
|
||||
|
||||
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