mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-05-31 00:00:10 +08:00
Use clang-formatter to format source code
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: Google
|
||||||
|
Standard: Cpp11
|
||||||
|
ColumnLimit: 0
|
||||||
|
AlignTrailingComments: false
|
||||||
|
NamespaceIndentation: All
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
SortIncludes: false
|
||||||
|
...
|
||||||
@@ -99,27 +99,27 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* itemName = [absolutePath lastPathComponent];
|
NSString* itemName = [absolutePath lastPathComponent];
|
||||||
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading item name \"%@\" is not allowed", itemName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading item name \"%@\" is not allowed", itemName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Because HEAD requests are mapped to GET ones, we need to handle directories but it's OK to return nothing per http://webdav.org/specs/rfc4918.html#rfc.section.9.4
|
// Because HEAD requests are mapped to GET ones, we need to handle directories but it's OK to return nothing per http://webdav.org/specs/rfc4918.html#rfc.section.9.4
|
||||||
if (isDirectory) {
|
if (isDirectory) {
|
||||||
return [GCDWebServerResponse response];
|
return [GCDWebServerResponse response];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(davServer:didDownloadFileAtPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(davServer:didDownloadFileAtPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate davServer:self didDownloadFileAtPath:absolutePath];
|
[self.delegate davServer:self didDownloadFileAtPath:absolutePath];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([request hasByteRange]) {
|
if ([request hasByteRange]) {
|
||||||
return [GCDWebServerFileResponse responseWithFile:absolutePath byteRange:request.byteRange];
|
return [GCDWebServerFileResponse responseWithFile:absolutePath byteRange:request.byteRange];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [GCDWebServerFileResponse responseWithFile:absolutePath];
|
return [GCDWebServerFileResponse responseWithFile:absolutePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
if ([request hasByteRange]) {
|
if ([request hasByteRange]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Range uploads not supported"];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Range uploads not supported"];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* relativePath = request.path;
|
NSString* relativePath = request.path;
|
||||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
if (![self _checkSandboxedPath:absolutePath]) {
|
if (![self _checkSandboxedPath:absolutePath]) {
|
||||||
@@ -137,27 +137,27 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
|
if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory];
|
BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory];
|
||||||
if (existing && isDirectory) {
|
if (existing && isDirectory) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"PUT not allowed on existing collection \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"PUT not allowed on existing collection \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* fileName = [absolutePath lastPathComponent];
|
NSString* fileName = [absolutePath lastPathComponent];
|
||||||
if (([fileName hasPrefix:@"."] && !_allowHidden) || ![self _checkFileExtension:fileName]) {
|
if (([fileName hasPrefix:@"."] && !_allowHidden) || ![self _checkFileExtension:fileName]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file name \"%@\" is not allowed", fileName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file name \"%@\" is not allowed", fileName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:request.temporaryPath]) {
|
if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:request.temporaryPath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file to \"%@\" is not permitted", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file to \"%@\" is not permitted", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:absolutePath error:NULL];
|
[[NSFileManager defaultManager] removeItemAtPath:absolutePath error:NULL];
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
if (![[NSFileManager defaultManager] moveItemAtPath:request.temporaryPath toPath:absolutePath error:&error]) {
|
if (![[NSFileManager defaultManager] moveItemAtPath:request.temporaryPath toPath:absolutePath error:&error]) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(davServer:didUploadFileAtPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(davServer:didUploadFileAtPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate davServer:self didUploadFileAtPath:absolutePath];
|
[self.delegate davServer:self didUploadFileAtPath:absolutePath];
|
||||||
@@ -171,28 +171,28 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
if (depthHeader && ![depthHeader isEqualToString:@"infinity"]) {
|
if (depthHeader && ![depthHeader isEqualToString:@"infinity"]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* relativePath = request.path;
|
NSString* relativePath = request.path;
|
||||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
BOOL isDirectory = NO;
|
BOOL isDirectory = NO;
|
||||||
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* itemName = [absolutePath lastPathComponent];
|
NSString* itemName = [absolutePath lastPathComponent];
|
||||||
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![self shouldDeleteItemAtPath:absolutePath]) {
|
if (![self shouldDeleteItemAtPath:absolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting \"%@\" is not permitted", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting \"%@\" is not permitted", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
if (![[NSFileManager defaultManager] removeItemAtPath:absolutePath error:&error]) {
|
if (![[NSFileManager defaultManager] removeItemAtPath:absolutePath error:&error]) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(davServer:didDeleteItemAtPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(davServer:didDeleteItemAtPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate davServer:self didDeleteItemAtPath:absolutePath];
|
[self.delegate davServer:self didDeleteItemAtPath:absolutePath];
|
||||||
@@ -205,7 +205,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
if ([request hasBody] && (request.contentLength > 0)) {
|
if ([request hasBody] && (request.contentLength > 0)) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_UnsupportedMediaType message:@"Unexpected request body for MKCOL method"];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_UnsupportedMediaType message:@"Unexpected request body for MKCOL method"];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* relativePath = request.path;
|
NSString* relativePath = request.path;
|
||||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
if (![self _checkSandboxedPath:absolutePath]) {
|
if (![self _checkSandboxedPath:absolutePath]) {
|
||||||
@@ -215,16 +215,16 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
|
if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* directoryName = [absolutePath lastPathComponent];
|
NSString* directoryName = [absolutePath lastPathComponent];
|
||||||
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
|
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![self shouldCreateDirectoryAtPath:absolutePath]) {
|
if (![self shouldCreateDirectoryAtPath:absolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory \"%@\" is not permitted", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory \"%@\" is not permitted", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:NO attributes:nil error:&error]) {
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:NO attributes:nil error:&error]) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath];
|
||||||
@@ -233,12 +233,12 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
NSString* creationDateHeader = [request.headers objectForKey:@"X-GCDWebServer-CreationDate"];
|
NSString* creationDateHeader = [request.headers objectForKey:@"X-GCDWebServer-CreationDate"];
|
||||||
if (creationDateHeader) {
|
if (creationDateHeader) {
|
||||||
NSDate* date = GCDWebServerParseISO8601(creationDateHeader);
|
NSDate* date = GCDWebServerParseISO8601(creationDateHeader);
|
||||||
if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate: date} ofItemAtPath:absolutePath error:&error]) {
|
if (!date || ![[NSFileManager defaultManager] setAttributes:@{ NSFileCreationDate : date } ofItemAtPath:absolutePath error:&error]) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed setting creation date for directory \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed setting creation date for directory \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(davServer:didCreateDirectoryAtPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(davServer:didCreateDirectoryAtPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate davServer:self didCreateDirectoryAtPath:absolutePath];
|
[self.delegate davServer:self didCreateDirectoryAtPath:absolutePath];
|
||||||
@@ -254,13 +254,13 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* srcRelativePath = request.path;
|
NSString* srcRelativePath = request.path;
|
||||||
NSString* srcAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:srcRelativePath];
|
NSString* srcAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:srcRelativePath];
|
||||||
if (![self _checkSandboxedPath:srcAbsolutePath]) {
|
if (![self _checkSandboxedPath:srcAbsolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* dstRelativePath = [request.headers objectForKey:@"Destination"];
|
NSString* dstRelativePath = [request.headers objectForKey:@"Destination"];
|
||||||
NSRange range = [dstRelativePath rangeOfString:[request.headers objectForKey:@"Host"]];
|
NSRange range = [dstRelativePath rangeOfString:[request.headers objectForKey:@"Host"]];
|
||||||
if ((dstRelativePath == nil) || (range.location == NSNotFound)) {
|
if ((dstRelativePath == nil) || (range.location == NSNotFound)) {
|
||||||
@@ -274,23 +274,23 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
if (![self _checkSandboxedPath:dstAbsolutePath]) {
|
if (![self _checkSandboxedPath:dstAbsolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL isDirectory;
|
BOOL isDirectory;
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:[dstAbsolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
|
if (![[NSFileManager defaultManager] fileExistsAtPath:[dstAbsolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Invalid destination \"%@\"", dstRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Invalid destination \"%@\"", dstRelativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* itemName = [dstAbsolutePath lastPathComponent];
|
NSString* itemName = [dstAbsolutePath lastPathComponent];
|
||||||
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"%@ to item name \"%@\" is not allowed", isMove ? @"Moving" : @"Copying", itemName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"%@ to item name \"%@\" is not allowed", isMove ? @"Moving" : @"Copying", itemName];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* overwriteHeader = [request.headers objectForKey:@"Overwrite"];
|
NSString* overwriteHeader = [request.headers objectForKey:@"Overwrite"];
|
||||||
BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:dstAbsolutePath];
|
BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:dstAbsolutePath];
|
||||||
if (existing && ((isMove && ![overwriteHeader isEqualToString:@"T"]) || (!isMove && [overwriteHeader isEqualToString:@"F"]))) {
|
if (existing && ((isMove && ![overwriteHeader isEqualToString:@"T"]) || (!isMove && [overwriteHeader isEqualToString:@"F"]))) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_PreconditionFailed message:@"Destination \"%@\" already exists", dstRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_PreconditionFailed message:@"Destination \"%@\" already exists", dstRelativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMove) {
|
if (isMove) {
|
||||||
if (![self shouldMoveItemFromPath:srcAbsolutePath toPath:dstAbsolutePath]) {
|
if (![self shouldMoveItemFromPath:srcAbsolutePath toPath:dstAbsolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving \"%@\" to \"%@\" is not permitted", srcRelativePath, dstRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving \"%@\" to \"%@\" is not permitted", srcRelativePath, dstRelativePath];
|
||||||
@@ -300,7 +300,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Copying \"%@\" to \"%@\" is not permitted", srcRelativePath, dstRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Copying \"%@\" to \"%@\" is not permitted", srcRelativePath, dstRelativePath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
if (isMove) {
|
if (isMove) {
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:dstAbsolutePath error:NULL];
|
[[NSFileManager defaultManager] removeItemAtPath:dstAbsolutePath error:NULL];
|
||||||
@@ -312,7 +312,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden underlyingError:error message:@"Failed copying \"%@\" to \"%@\"", srcRelativePath, dstRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden underlyingError:error message:@"Failed copying \"%@\" to \"%@\"", srcRelativePath, dstRelativePath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMove) {
|
if (isMove) {
|
||||||
if ([self.delegate respondsToSelector:@selector(davServer:didMoveItemFromPath:toPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(davServer:didMoveItemFromPath:toPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
@@ -326,7 +326,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [GCDWebServerResponse responseWithStatusCode:(existing ? kGCDWebServerHTTPStatusCode_NoContent : kGCDWebServerHTTPStatusCode_Created)];
|
return [GCDWebServerResponse responseWithStatusCode:(existing ? kGCDWebServerHTTPStatusCode_NoContent : kGCDWebServerHTTPStatusCode_Created)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +355,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
[xmlString appendFormat:@"<D:href>%@</D:href>", escapedPath];
|
[xmlString appendFormat:@"<D:href>%@</D:href>", escapedPath];
|
||||||
[xmlString appendString:@"<D:propstat>"];
|
[xmlString appendString:@"<D:propstat>"];
|
||||||
[xmlString appendString:@"<D:prop>"];
|
[xmlString appendString:@"<D:prop>"];
|
||||||
|
|
||||||
if (properties & kDAVProperty_ResourceType) {
|
if (properties & kDAVProperty_ResourceType) {
|
||||||
if (isDirectory) {
|
if (isDirectory) {
|
||||||
[xmlString appendString:@"<D:resourcetype><D:collection/></D:resourcetype>"];
|
[xmlString appendString:@"<D:resourcetype><D:collection/></D:resourcetype>"];
|
||||||
@@ -363,19 +363,19 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
[xmlString appendString:@"<D:resourcetype/>"];
|
[xmlString appendString:@"<D:resourcetype/>"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((properties & kDAVProperty_CreationDate) && [attributes objectForKey:NSFileCreationDate]) {
|
if ((properties & kDAVProperty_CreationDate) && [attributes objectForKey:NSFileCreationDate]) {
|
||||||
[xmlString appendFormat:@"<D:creationdate>%@</D:creationdate>", GCDWebServerFormatISO8601([attributes fileCreationDate])];
|
[xmlString appendFormat:@"<D:creationdate>%@</D:creationdate>", GCDWebServerFormatISO8601([attributes fileCreationDate])];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((properties & kDAVProperty_LastModified) && isFile && [attributes objectForKey:NSFileModificationDate]) { // Last modification date is not useful for directories as it changes implicitely and 'Last-Modified' header is not provided for directories anyway
|
if ((properties & kDAVProperty_LastModified) && isFile && [attributes objectForKey:NSFileModificationDate]) { // Last modification date is not useful for directories as it changes implicitely and 'Last-Modified' header is not provided for directories anyway
|
||||||
[xmlString appendFormat:@"<D:getlastmodified>%@</D:getlastmodified>", GCDWebServerFormatRFC822([attributes fileModificationDate])];
|
[xmlString appendFormat:@"<D:getlastmodified>%@</D:getlastmodified>", GCDWebServerFormatRFC822([attributes fileModificationDate])];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((properties & kDAVProperty_ContentLength) && !isDirectory && [attributes objectForKey:NSFileSize]) {
|
if ((properties & kDAVProperty_ContentLength) && !isDirectory && [attributes objectForKey:NSFileSize]) {
|
||||||
[xmlString appendFormat:@"<D:getcontentlength>%llu</D:getcontentlength>", [attributes fileSize]];
|
[xmlString appendFormat:@"<D:getcontentlength>%llu</D:getcontentlength>", [attributes fileSize]];
|
||||||
}
|
}
|
||||||
|
|
||||||
[xmlString appendString:@"</D:prop>"];
|
[xmlString appendString:@"</D:prop>"];
|
||||||
[xmlString appendString:@"<D:status>HTTP/1.1 200 OK</D:status>"];
|
[xmlString appendString:@"<D:status>HTTP/1.1 200 OK</D:status>"];
|
||||||
[xmlString appendString:@"</D:propstat>"];
|
[xmlString appendString:@"</D:propstat>"];
|
||||||
@@ -397,7 +397,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
} else {
|
} else {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader]; // TODO: Return 403 / propfind-finite-depth for "infinity" depth
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader]; // TODO: Return 403 / propfind-finite-depth for "infinity" depth
|
||||||
}
|
}
|
||||||
|
|
||||||
DAVProperties properties = 0;
|
DAVProperties properties = 0;
|
||||||
if (request.data.length) {
|
if (request.data.length) {
|
||||||
BOOL success = YES;
|
BOOL success = YES;
|
||||||
@@ -438,19 +438,19 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
} else {
|
} else {
|
||||||
properties = kDAVAllProperties;
|
properties = kDAVAllProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* relativePath = request.path;
|
NSString* relativePath = request.path;
|
||||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
BOOL isDirectory = NO;
|
BOOL isDirectory = NO;
|
||||||
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* itemName = [absolutePath lastPathComponent];
|
NSString* itemName = [absolutePath lastPathComponent];
|
||||||
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Retrieving properties for item name \"%@\" is not allowed", itemName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Retrieving properties for item name \"%@\" is not allowed", itemName];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray* items = nil;
|
NSArray* items = nil;
|
||||||
if (isDirectory) {
|
if (isDirectory) {
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
@@ -459,7 +459,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
|
NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
|
||||||
[xmlString appendString:@"<D:multistatus xmlns:D=\"DAV:\">\n"];
|
[xmlString appendString:@"<D:multistatus xmlns:D=\"DAV:\">\n"];
|
||||||
if (![relativePath hasPrefix:@"/"]) {
|
if (![relativePath hasPrefix:@"/"]) {
|
||||||
@@ -477,7 +477,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
[xmlString appendString:@"</D:multistatus>"];
|
[xmlString appendString:@"</D:multistatus>"];
|
||||||
|
|
||||||
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding]
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
contentType:@"application/xml; charset=\"utf-8\""];
|
contentType:@"application/xml; charset=\"utf-8\""];
|
||||||
response.statusCode = kGCDWebServerHTTPStatusCode_MultiStatus;
|
response.statusCode = kGCDWebServerHTTPStatusCode_MultiStatus;
|
||||||
@@ -488,14 +488,14 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
if (!_IsMacFinder(request)) {
|
if (!_IsMacFinder(request)) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"LOCK method only allowed for Mac Finder"];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"LOCK method only allowed for Mac Finder"];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* relativePath = request.path;
|
NSString* relativePath = request.path;
|
||||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
BOOL isDirectory = NO;
|
BOOL isDirectory = NO;
|
||||||
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* depthHeader = [request.headers objectForKey:@"Depth"];
|
NSString* depthHeader = [request.headers objectForKey:@"Depth"];
|
||||||
NSString* timeoutHeader = [request.headers objectForKey:@"Timeout"];
|
NSString* timeoutHeader = [request.headers objectForKey:@"Timeout"];
|
||||||
NSString* scope = nil;
|
NSString* scope = nil;
|
||||||
@@ -533,16 +533,16 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
NSString* string = [[NSString alloc] initWithData:request.data encoding:NSUTF8StringEncoding];
|
NSString* string = [[NSString alloc] initWithData:request.data encoding:NSUTF8StringEncoding];
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Invalid DAV properties:\n%@", string];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Invalid DAV properties:\n%@", string];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![scope isEqualToString:@"exclusive"] || ![type isEqualToString:@"write"] || ![depthHeader isEqualToString:@"0"]) {
|
if (![scope isEqualToString:@"exclusive"] || ![type isEqualToString:@"write"] || ![depthHeader isEqualToString:@"0"]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Locking request \"%@/%@/%@\" for \"%@\" is not allowed", scope, type, depthHeader, relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Locking request \"%@/%@/%@\" for \"%@\" is not allowed", scope, type, depthHeader, relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* itemName = [absolutePath lastPathComponent];
|
NSString* itemName = [absolutePath lastPathComponent];
|
||||||
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Locking item name \"%@\" is not allowed", itemName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Locking item name \"%@\" is not allowed", itemName];
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||||
NSString* lockTokenHeader = [request.headers objectForKey:@"X-GCDWebServer-LockToken"];
|
NSString* lockTokenHeader = [request.headers objectForKey:@"X-GCDWebServer-LockToken"];
|
||||||
if (lockTokenHeader) {
|
if (lockTokenHeader) {
|
||||||
@@ -556,7 +556,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
CFRelease(string);
|
CFRelease(string);
|
||||||
CFRelease(uuid);
|
CFRelease(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
|
NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
|
||||||
[xmlString appendString:@"<D:prop xmlns:D=\"DAV:\">\n"];
|
[xmlString appendString:@"<D:prop xmlns:D=\"DAV:\">\n"];
|
||||||
[xmlString appendString:@"<D:lockdiscovery>\n<D:activelock>\n"];
|
[xmlString appendString:@"<D:lockdiscovery>\n<D:activelock>\n"];
|
||||||
@@ -574,7 +574,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
[xmlString appendFormat:@"<D:lockroot><D:href>%@</D:href></D:lockroot>\n", lockroot];
|
[xmlString appendFormat:@"<D:lockroot><D:href>%@</D:href></D:lockroot>\n", lockroot];
|
||||||
[xmlString appendString:@"</D:activelock>\n</D:lockdiscovery>\n"];
|
[xmlString appendString:@"</D:activelock>\n</D:lockdiscovery>\n"];
|
||||||
[xmlString appendString:@"</D:prop>"];
|
[xmlString appendString:@"</D:prop>"];
|
||||||
|
|
||||||
[self logVerbose:@"WebDAV pretending to lock \"%@\"", relativePath];
|
[self logVerbose:@"WebDAV pretending to lock \"%@\"", relativePath];
|
||||||
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding]
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
contentType:@"application/xml; charset=\"utf-8\""];
|
contentType:@"application/xml; charset=\"utf-8\""];
|
||||||
@@ -585,24 +585,24 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
if (!_IsMacFinder(request)) {
|
if (!_IsMacFinder(request)) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"UNLOCK method only allowed for Mac Finder"];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"UNLOCK method only allowed for Mac Finder"];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* relativePath = request.path;
|
NSString* relativePath = request.path;
|
||||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
BOOL isDirectory = NO;
|
BOOL isDirectory = NO;
|
||||||
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* tokenHeader = [request.headers objectForKey:@"Lock-Token"];
|
NSString* tokenHeader = [request.headers objectForKey:@"Lock-Token"];
|
||||||
if (!tokenHeader.length) {
|
if (!tokenHeader.length) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Missing 'Lock-Token' header"];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Missing 'Lock-Token' header"];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* itemName = [absolutePath lastPathComponent];
|
NSString* itemName = [absolutePath lastPathComponent];
|
||||||
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Unlocking item name \"%@\" is not allowed", itemName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Unlocking item name \"%@\" is not allowed", itemName];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self logVerbose:@"WebDAV pretending to unlock \"%@\"", relativePath];
|
[self logVerbose:@"WebDAV pretending to unlock \"%@\"", relativePath];
|
||||||
return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NoContent];
|
return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NoContent];
|
||||||
}
|
}
|
||||||
@@ -611,7 +611,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
|
|
||||||
@implementation GCDWebDAVServer
|
@implementation GCDWebDAVServer
|
||||||
|
|
||||||
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden;
|
@synthesize uploadDirectory = _uploadDirectory, allowedFileExtensions = _allowedExtensions, allowHiddenItems = _allowHidden;
|
||||||
|
|
||||||
@dynamic delegate;
|
@dynamic delegate;
|
||||||
|
|
||||||
@@ -619,57 +619,76 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
||||||
GCDWebDAVServer* __unsafe_unretained server = self;
|
GCDWebDAVServer* __unsafe_unretained server = self;
|
||||||
|
|
||||||
// 9.1 PROPFIND method
|
// 9.1 PROPFIND method
|
||||||
[self addDefaultHandlerForMethod:@"PROPFIND" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"PROPFIND"
|
||||||
return [server performPROPFIND:(GCDWebServerDataRequest*)request];
|
requestClass:[GCDWebServerDataRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performPROPFIND:(GCDWebServerDataRequest*)request];
|
||||||
|
}];
|
||||||
|
|
||||||
// 9.3 MKCOL Method
|
// 9.3 MKCOL Method
|
||||||
[self addDefaultHandlerForMethod:@"MKCOL" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"MKCOL"
|
||||||
return [server performMKCOL:(GCDWebServerDataRequest*)request];
|
requestClass:[GCDWebServerDataRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performMKCOL:(GCDWebServerDataRequest*)request];
|
||||||
|
}];
|
||||||
|
|
||||||
// 9.4 GET & HEAD methods
|
// 9.4 GET & HEAD methods
|
||||||
[self addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"GET"
|
||||||
return [server performGET:request];
|
requestClass:[GCDWebServerRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performGET:request];
|
||||||
|
}];
|
||||||
|
|
||||||
// 9.6 DELETE method
|
// 9.6 DELETE method
|
||||||
[self addDefaultHandlerForMethod:@"DELETE" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"DELETE"
|
||||||
return [server performDELETE:request];
|
requestClass:[GCDWebServerRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performDELETE:request];
|
||||||
|
}];
|
||||||
|
|
||||||
// 9.7 PUT method
|
// 9.7 PUT method
|
||||||
[self addDefaultHandlerForMethod:@"PUT" requestClass:[GCDWebServerFileRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"PUT"
|
||||||
return [server performPUT:(GCDWebServerFileRequest*)request];
|
requestClass:[GCDWebServerFileRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performPUT:(GCDWebServerFileRequest*)request];
|
||||||
|
}];
|
||||||
|
|
||||||
// 9.8 COPY method
|
// 9.8 COPY method
|
||||||
[self addDefaultHandlerForMethod:@"COPY" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"COPY"
|
||||||
return [server performCOPY:request isMove:NO];
|
requestClass:[GCDWebServerRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performCOPY:request isMove:NO];
|
||||||
|
}];
|
||||||
|
|
||||||
// 9.9 MOVE method
|
// 9.9 MOVE method
|
||||||
[self addDefaultHandlerForMethod:@"MOVE" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"MOVE"
|
||||||
return [server performCOPY:request isMove:YES];
|
requestClass:[GCDWebServerRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performCOPY:request isMove:YES];
|
||||||
|
}];
|
||||||
|
|
||||||
// 9.10 LOCK method
|
// 9.10 LOCK method
|
||||||
[self addDefaultHandlerForMethod:@"LOCK" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"LOCK"
|
||||||
return [server performLOCK:(GCDWebServerDataRequest*)request];
|
requestClass:[GCDWebServerDataRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performLOCK:(GCDWebServerDataRequest*)request];
|
||||||
|
}];
|
||||||
|
|
||||||
// 9.11 UNLOCK method
|
// 9.11 UNLOCK method
|
||||||
[self addDefaultHandlerForMethod:@"UNLOCK" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"UNLOCK"
|
||||||
return [server performUNLOCK:request];
|
requestClass:[GCDWebServerRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performUNLOCK:request];
|
||||||
|
}];
|
||||||
|
|
||||||
// 10.1 OPTIONS method / DAV Header
|
// 10.1 OPTIONS method / DAV Header
|
||||||
[self addDefaultHandlerForMethod:@"OPTIONS" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"OPTIONS"
|
||||||
return [server performOPTIONS:request];
|
requestClass:[GCDWebServerRequest class]
|
||||||
}];
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server performOPTIONS:request];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -575,22 +575,22 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
/**
|
/**
|
||||||
* Logs a message to the logging facility at the VERBOSE level.
|
* Logs a message to the logging facility at the VERBOSE level.
|
||||||
*/
|
*/
|
||||||
- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a message to the logging facility at the INFO level.
|
* Logs a message to the logging facility at the INFO level.
|
||||||
*/
|
*/
|
||||||
- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a message to the logging facility at the WARNING level.
|
* Logs a message to the logging facility at the WARNING level.
|
||||||
*/
|
*/
|
||||||
- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a message to the logging facility at the ERROR level.
|
* Logs a message to the logging facility at the ERROR level.
|
||||||
*/
|
*/
|
||||||
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
+144
-125
@@ -141,7 +141,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
|
|
||||||
@implementation GCDWebServerHandler
|
@implementation GCDWebServerHandler
|
||||||
|
|
||||||
@synthesize matchBlock=_matchBlock, asyncProcessBlock=_asyncProcessBlock;
|
@synthesize matchBlock = _matchBlock, asyncProcessBlock = _asyncProcessBlock;
|
||||||
|
|
||||||
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
@@ -162,7 +162,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
NSInteger _activeConnections; // Accessed through _syncQueue only
|
NSInteger _activeConnections; // Accessed through _syncQueue only
|
||||||
BOOL _connected; // Accessed on main thread only
|
BOOL _connected; // Accessed on main thread only
|
||||||
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
|
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
|
||||||
|
|
||||||
NSDictionary* _options;
|
NSDictionary* _options;
|
||||||
NSString* _serverName;
|
NSString* _serverName;
|
||||||
NSString* _authenticationRealm;
|
NSString* _authenticationRealm;
|
||||||
@@ -195,9 +195,9 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
|
|
||||||
@implementation GCDWebServer
|
@implementation GCDWebServer
|
||||||
|
|
||||||
@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm,
|
@synthesize delegate = _delegate, handlers = _handlers, port = _port, serverName = _serverName, authenticationRealm = _authenticationRealm,
|
||||||
authenticationBasicAccounts=_authenticationBasicAccounts, authenticationDigestAccounts=_authenticationDigestAccounts,
|
authenticationBasicAccounts = _authenticationBasicAccounts, authenticationDigestAccounts = _authenticationDigestAccounts,
|
||||||
shouldAutomaticallyMapHEADToGET=_mapHEADToGET, dispatchQueuePriority=_dispatchQueuePriority;
|
shouldAutomaticallyMapHEADToGET = _mapHEADToGET, dispatchQueuePriority = _dispatchQueuePriority;
|
||||||
|
|
||||||
+ (void)initialize {
|
+ (void)initialize {
|
||||||
GCDWebServerInitializeFunctions();
|
GCDWebServerInitializeFunctions();
|
||||||
@@ -220,7 +220,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
GWS_DCHECK(_activeConnections == 0);
|
GWS_DCHECK(_activeConnections == 0);
|
||||||
GWS_DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
|
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
|
GWS_DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle
|
||||||
|
|
||||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||||
dispatch_release(_sourceGroup);
|
dispatch_release(_sourceGroup);
|
||||||
dispatch_release(_syncQueue);
|
dispatch_release(_syncQueue);
|
||||||
@@ -235,10 +235,10 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
if (_backgroundTask == UIBackgroundTaskInvalid) {
|
if (_backgroundTask == UIBackgroundTaskInvalid) {
|
||||||
GWS_LOG_DEBUG(@"Did start background task");
|
GWS_LOG_DEBUG(@"Did start background task");
|
||||||
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
||||||
|
|
||||||
GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]);
|
GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]);
|
||||||
[self _endBackgroundTask];
|
[self _endBackgroundTask];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
@@ -253,13 +253,13 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
GWS_DCHECK(_connected == NO);
|
GWS_DCHECK(_connected == NO);
|
||||||
_connected = YES;
|
_connected = YES;
|
||||||
GWS_LOG_DEBUG(@"Did connect");
|
GWS_LOG_DEBUG(@"Did connect");
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
||||||
[self _startBackgroundTask];
|
[self _startBackgroundTask];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
|
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
|
||||||
[_delegate webServerDidConnect:self];
|
[_delegate webServerDidConnect:self];
|
||||||
}
|
}
|
||||||
@@ -267,7 +267,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
|
|
||||||
- (void)willStartConnection:(GCDWebServerConnection*)connection {
|
- (void)willStartConnection:(GCDWebServerConnection*)connection {
|
||||||
dispatch_sync(_syncQueue, ^{
|
dispatch_sync(_syncQueue, ^{
|
||||||
|
|
||||||
GWS_DCHECK(_activeConnections >= 0);
|
GWS_DCHECK(_activeConnections >= 0);
|
||||||
if (_activeConnections == 0) {
|
if (_activeConnections == 0) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
@@ -282,7 +282,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
_activeConnections += 1;
|
_activeConnections += 1;
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,11 +309,11 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
GWS_DCHECK(_connected == YES);
|
GWS_DCHECK(_connected == YES);
|
||||||
_connected = NO;
|
_connected = NO;
|
||||||
GWS_LOG_DEBUG(@"Did disconnect");
|
GWS_LOG_DEBUG(@"Did disconnect");
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
[self _endBackgroundTask];
|
[self _endBackgroundTask];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) {
|
if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) {
|
||||||
[_delegate webServerDidDisconnect:self];
|
[_delegate webServerDidDisconnect:self];
|
||||||
}
|
}
|
||||||
@@ -356,9 +356,10 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
||||||
[self addHandlerWithMatchBlock:matchBlock asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
[self addHandlerWithMatchBlock:matchBlock
|
||||||
completionBlock(processBlock(request));
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
}];
|
completionBlock(processBlock(request));
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
||||||
@@ -464,7 +465,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
if (listeningSocket > 0) {
|
if (listeningSocket > 0) {
|
||||||
int yes = 1;
|
int yes = 1;
|
||||||
setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||||
|
|
||||||
if (bind(listeningSocket, address, length) == 0) {
|
if (bind(listeningSocket, address, length) == 0) {
|
||||||
if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
|
if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
|
||||||
GWS_LOG_DEBUG(@"Did open %s listening socket %i", useIPv6 ? "IPv6" : "IPv4", listeningSocket);
|
GWS_LOG_DEBUG(@"Did open %s listening socket %i", useIPv6 ? "IPv6" : "IPv4", listeningSocket);
|
||||||
@@ -483,7 +484,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
||||||
close(listeningSocket);
|
close(listeningSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (error) {
|
if (error) {
|
||||||
*error = GCDWebServerMakePosixError(errno);
|
*error = GCDWebServerMakePosixError(errno);
|
||||||
@@ -497,7 +498,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
dispatch_group_enter(_sourceGroup);
|
dispatch_group_enter(_sourceGroup);
|
||||||
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0));
|
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0));
|
||||||
dispatch_source_set_cancel_handler(source, ^{
|
dispatch_source_set_cancel_handler(source, ^{
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
int result = close(listeningSocket);
|
int result = close(listeningSocket);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
@@ -507,17 +508,17 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dispatch_group_leave(_sourceGroup);
|
dispatch_group_leave(_sourceGroup);
|
||||||
|
|
||||||
});
|
});
|
||||||
dispatch_source_set_event_handler(source, ^{
|
dispatch_source_set_event_handler(source, ^{
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
struct sockaddr_storage remoteSockAddr;
|
struct sockaddr_storage remoteSockAddr;
|
||||||
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
|
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
|
||||||
int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen);
|
int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen);
|
||||||
if (socket > 0) {
|
if (socket > 0) {
|
||||||
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
|
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
|
||||||
|
|
||||||
struct sockaddr_storage localSockAddr;
|
struct sockaddr_storage localSockAddr;
|
||||||
socklen_t localAddrLen = sizeof(localSockAddr);
|
socklen_t localAddrLen = sizeof(localSockAddr);
|
||||||
NSData* localAddress = nil;
|
NSData* localAddress = nil;
|
||||||
@@ -527,28 +528,28 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
} else {
|
} else {
|
||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
int noSigPipe = 1;
|
int noSigPipe = 1;
|
||||||
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE
|
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE
|
||||||
|
|
||||||
GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
|
GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
|
||||||
[connection self]; // Prevent compiler from complaining about unused variable / useless statement
|
[connection self]; // Prevent compiler from complaining about unused variable / useless statement
|
||||||
} else {
|
} else {
|
||||||
GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_start:(NSError**)error {
|
- (BOOL)_start:(NSError**)error {
|
||||||
GWS_DCHECK(_source4 == NULL);
|
GWS_DCHECK(_source4 == NULL);
|
||||||
|
|
||||||
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
||||||
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
|
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
|
||||||
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
|
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
|
||||||
|
|
||||||
struct sockaddr_in addr4;
|
struct sockaddr_in addr4;
|
||||||
bzero(&addr4, sizeof(addr4));
|
bzero(&addr4, sizeof(addr4));
|
||||||
addr4.sin_len = sizeof(addr4);
|
addr4.sin_len = sizeof(addr4);
|
||||||
@@ -568,7 +569,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
|
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in6 addr6;
|
struct sockaddr_in6 addr6;
|
||||||
bzero(&addr6, sizeof(addr6));
|
bzero(&addr6, sizeof(addr6));
|
||||||
addr6.sin6_len = sizeof(addr6);
|
addr6.sin6_len = sizeof(addr6);
|
||||||
@@ -580,7 +581,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
close(listeningSocket4);
|
close(listeningSocket4);
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
|
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
|
||||||
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
|
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
|
||||||
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
|
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
|
||||||
@@ -602,24 +603,24 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
|
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
|
||||||
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
|
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
|
||||||
_dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue];
|
_dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue];
|
||||||
|
|
||||||
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
|
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
|
||||||
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
|
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
|
||||||
_port = port;
|
_port = port;
|
||||||
_bindToLocalhost = bindToLocalhost;
|
_bindToLocalhost = bindToLocalhost;
|
||||||
|
|
||||||
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil);
|
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil);
|
||||||
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
||||||
if (bonjourName) {
|
if (bonjourName) {
|
||||||
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (__bridge CFStringRef)bonjourType, (__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) {
|
if (_registrationService) {
|
||||||
CFNetServiceClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
CFNetServiceClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
||||||
|
|
||||||
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
|
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
|
||||||
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
CFStreamError streamError = {0};
|
CFStreamError streamError = {0};
|
||||||
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
|
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
|
||||||
|
|
||||||
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
|
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
|
||||||
if (_resolutionService) {
|
if (_resolutionService) {
|
||||||
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
||||||
@@ -631,7 +632,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
GWS_LOG_ERROR(@"Failed creating CFNetService for registration");
|
GWS_LOG_ERROR(@"Failed creating CFNetService for registration");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
|
if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
|
||||||
DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self);
|
DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self);
|
||||||
if (status == kDNSServiceErr_NoError) {
|
if (status == kDNSServiceErr_NoError) {
|
||||||
@@ -654,7 +655,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status);
|
GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_resume(_source4);
|
dispatch_resume(_source4);
|
||||||
dispatch_resume(_source6);
|
dispatch_resume(_source6);
|
||||||
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
|
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
|
||||||
@@ -663,13 +664,13 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
[_delegate webServerDidStart:self];
|
[_delegate webServerDidStart:self];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_stop {
|
- (void)_stop {
|
||||||
GWS_DCHECK(_source4 != NULL);
|
GWS_DCHECK(_source4 != NULL);
|
||||||
|
|
||||||
if (_dnsService) {
|
if (_dnsService) {
|
||||||
_dnsAddress = nil;
|
_dnsAddress = nil;
|
||||||
_dnsPort = 0;
|
_dnsPort = 0;
|
||||||
@@ -685,7 +686,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
DNSServiceRefDeallocate(_dnsService);
|
DNSServiceRefDeallocate(_dnsService);
|
||||||
_dnsService = NULL;
|
_dnsService = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_registrationService) {
|
if (_registrationService) {
|
||||||
if (_resolutionService) {
|
if (_resolutionService) {
|
||||||
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
@@ -700,7 +701,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
CFRelease(_registrationService);
|
CFRelease(_registrationService);
|
||||||
_registrationService = NULL;
|
_registrationService = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_source_cancel(_source6);
|
dispatch_source_cancel(_source6);
|
||||||
dispatch_source_cancel(_source4);
|
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
|
dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed
|
||||||
@@ -714,12 +715,12 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
_source4 = NULL;
|
_source4 = NULL;
|
||||||
_port = 0;
|
_port = 0;
|
||||||
_bindToLocalhost = NO;
|
_bindToLocalhost = NO;
|
||||||
|
|
||||||
_serverName = nil;
|
_serverName = nil;
|
||||||
_authenticationRealm = nil;
|
_authenticationRealm = nil;
|
||||||
_authenticationBasicAccounts = nil;
|
_authenticationBasicAccounts = nil;
|
||||||
_authenticationDigestAccounts = nil;
|
_authenticationDigestAccounts = nil;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
if (_disconnectTimer) {
|
if (_disconnectTimer) {
|
||||||
CFRunLoopTimerInvalidate(_disconnectTimer);
|
CFRunLoopTimerInvalidate(_disconnectTimer);
|
||||||
@@ -728,7 +729,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
[self _didDisconnect];
|
[self _didDisconnect];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
GWS_LOG_INFO(@"%@ stopped", [self class]);
|
GWS_LOG_INFO(@"%@ stopped", [self class]);
|
||||||
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
|
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
@@ -896,32 +897,38 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
@implementation GCDWebServer (Handlers)
|
@implementation GCDWebServer (Handlers)
|
||||||
|
|
||||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
||||||
[self addDefaultHandlerForMethod:method requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
[self addDefaultHandlerForMethod:method
|
||||||
completionBlock(block(request));
|
requestClass:aClass
|
||||||
}];
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
|
completionBlock(block(request));
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
||||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
|
|
||||||
if (![requestMethod isEqualToString:method]) {
|
if (![requestMethod isEqualToString:method]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||||
|
|
||||||
} asyncProcessBlock:block];
|
}
|
||||||
|
asyncProcessBlock:block];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
||||||
[self addHandlerForMethod:method path:path requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
[self addHandlerForMethod:method
|
||||||
completionBlock(block(request));
|
path:path
|
||||||
}];
|
requestClass:aClass
|
||||||
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
|
completionBlock(block(request));
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
||||||
if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
||||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
|
|
||||||
if (![requestMethod isEqualToString:method]) {
|
if (![requestMethod isEqualToString:method]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@@ -929,24 +936,28 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return [[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];
|
}
|
||||||
|
asyncProcessBlock:block];
|
||||||
} else {
|
} else {
|
||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
||||||
[self addHandlerForMethod:method pathRegex:regex requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
[self addHandlerForMethod:method
|
||||||
completionBlock(block(request));
|
pathRegex:regex
|
||||||
}];
|
requestClass:aClass
|
||||||
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
|
completionBlock(block(request));
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
||||||
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL];
|
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL];
|
||||||
if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
||||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
|
|
||||||
if (![requestMethod isEqualToString:method]) {
|
if (![requestMethod isEqualToString:method]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@@ -972,8 +983,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||||
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
|
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
|
||||||
return request;
|
return request;
|
||||||
|
|
||||||
} asyncProcessBlock:block];
|
}
|
||||||
|
asyncProcessBlock:block];
|
||||||
} else {
|
} else {
|
||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -984,29 +996,35 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
@implementation GCDWebServer (GETHandlers)
|
@implementation GCDWebServer (GETHandlers)
|
||||||
|
|
||||||
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge {
|
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge {
|
||||||
[self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"GET"
|
||||||
|
path:path
|
||||||
GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
|
requestClass:[GCDWebServerRequest class]
|
||||||
response.cacheControlMaxAge = cacheAge;
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
return response;
|
|
||||||
|
GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
|
||||||
}];
|
response.cacheControlMaxAge = cacheAge;
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
||||||
[self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"GET"
|
||||||
|
path:path
|
||||||
GCDWebServerResponse* response = nil;
|
requestClass:[GCDWebServerRequest class]
|
||||||
if (allowRangeRequests) {
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
|
|
||||||
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
GCDWebServerResponse* response = nil;
|
||||||
} else {
|
if (allowRangeRequests) {
|
||||||
response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment];
|
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
|
||||||
}
|
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||||
response.cacheControlMaxAge = cacheAge;
|
} else {
|
||||||
return response;
|
response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment];
|
||||||
|
}
|
||||||
}];
|
response.cacheControlMaxAge = cacheAge;
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
|
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
|
||||||
@@ -1042,8 +1060,8 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
||||||
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
|
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
|
||||||
GCDWebServer* __unsafe_unretained server = self;
|
GCDWebServer* __unsafe_unretained server = self;
|
||||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
|
|
||||||
if (![requestMethod isEqualToString:@"GET"]) {
|
if (![requestMethod isEqualToString:@"GET"]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@@ -1051,39 +1069,40 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||||
|
|
||||||
} processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
}
|
||||||
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
GCDWebServerResponse* response = nil;
|
|
||||||
NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
GCDWebServerResponse* response = nil;
|
||||||
NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
|
NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
||||||
if (fileType) {
|
NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
|
||||||
if ([fileType isEqualToString:NSFileTypeDirectory]) {
|
if (fileType) {
|
||||||
if (indexFilename) {
|
if ([fileType isEqualToString:NSFileTypeDirectory]) {
|
||||||
NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
if (indexFilename) {
|
||||||
NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType];
|
NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
||||||
if ([indexType isEqualToString:NSFileTypeRegular]) {
|
NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType];
|
||||||
return [GCDWebServerFileResponse responseWithFile:indexPath];
|
if ([indexType isEqualToString:NSFileTypeRegular]) {
|
||||||
|
return [GCDWebServerFileResponse responseWithFile:indexPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = [server _responseWithContentsOfDirectory:filePath];
|
||||||
|
} else if ([fileType isEqualToString:NSFileTypeRegular]) {
|
||||||
|
if (allowRangeRequests) {
|
||||||
|
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
||||||
|
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||||
|
} else {
|
||||||
|
response = [GCDWebServerFileResponse responseWithFile:filePath];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response = [server _responseWithContentsOfDirectory:filePath];
|
if (response) {
|
||||||
} else if ([fileType isEqualToString:NSFileTypeRegular]) {
|
response.cacheControlMaxAge = cacheAge;
|
||||||
if (allowRangeRequests) {
|
|
||||||
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
|
||||||
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
|
||||||
} else {
|
} else {
|
||||||
response = [GCDWebServerFileResponse responseWithFile:filePath];
|
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
|
||||||
}
|
}
|
||||||
}
|
return response;
|
||||||
}
|
|
||||||
if (response) {
|
}];
|
||||||
response.cacheControlMaxAge = cacheAge;
|
|
||||||
} else {
|
|
||||||
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}];
|
|
||||||
} else {
|
} else {
|
||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -1204,11 +1223,11 @@ static void _LogResult(NSString* format, ...) {
|
|||||||
|
|
||||||
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path {
|
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path {
|
||||||
GWS_DCHECK([NSThread isMainThread]);
|
GWS_DCHECK([NSThread isMainThread]);
|
||||||
NSArray* ignoredHeaders = @[@"Date", @"Etag"]; // Dates are always different by definition and ETags depend on file system node IDs
|
NSArray* ignoredHeaders = @[ @"Date", @"Etag" ]; // Dates are always different by definition and ETags depend on file system node IDs
|
||||||
NSInteger result = -1;
|
NSInteger result = -1;
|
||||||
if ([self startWithOptions:options error:NULL]) {
|
if ([self startWithOptions:options error:NULL]) {
|
||||||
_ExecuteMainThreadRunLoopSources();
|
_ExecuteMainThreadRunLoopSources();
|
||||||
|
|
||||||
result = 0;
|
result = 0;
|
||||||
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
|
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
|
||||||
for (NSString* requestFile in files) {
|
for (NSString* requestFile in files) {
|
||||||
@@ -1230,19 +1249,19 @@ static void _LogResult(NSString* format, ...) {
|
|||||||
if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
|
if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
|
||||||
NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]];
|
NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]];
|
||||||
if (responseData) {
|
if (responseData) {
|
||||||
CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
|
CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
|
||||||
if (expectedResponse) {
|
if (expectedResponse) {
|
||||||
CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, self.port);
|
CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, self.port);
|
||||||
if (actualResponse) {
|
if (actualResponse) {
|
||||||
success = YES;
|
success = YES;
|
||||||
|
|
||||||
CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
|
CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
|
||||||
CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
|
CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
|
||||||
if (actualStatusCode != expectedStatusCode) {
|
if (actualStatusCode != expectedStatusCode) {
|
||||||
_LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
|
_LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
|
||||||
success = NO;
|
success = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSDictionary* expectedHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
NSDictionary* expectedHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
||||||
NSDictionary* actualHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
NSDictionary* actualHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
||||||
for (NSString* expectedHeader in expectedHeaders) {
|
for (NSString* expectedHeader in expectedHeaders) {
|
||||||
@@ -1262,7 +1281,7 @@ static void _LogResult(NSString* format, ...) {
|
|||||||
success = NO;
|
success = NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length")));
|
NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length")));
|
||||||
NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse));
|
NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse));
|
||||||
NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length")));
|
NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length")));
|
||||||
@@ -1281,14 +1300,14 @@ static void _LogResult(NSString* format, ...) {
|
|||||||
if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
|
if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
|
||||||
NSTask* task = [[NSTask alloc] init];
|
NSTask* task = [[NSTask alloc] init];
|
||||||
[task setLaunchPath:@"/usr/bin/opendiff"];
|
[task setLaunchPath:@"/usr/bin/opendiff"];
|
||||||
[task setArguments:@[expectedPath, actualPath]];
|
[task setArguments:@[ expectedPath, actualPath ]];
|
||||||
[task launch];
|
[task launch];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CFRelease(actualResponse);
|
CFRelease(actualResponse);
|
||||||
}
|
}
|
||||||
CFRelease(expectedResponse);
|
CFRelease(expectedResponse);
|
||||||
@@ -1311,9 +1330,9 @@ static void _LogResult(NSString* format, ...) {
|
|||||||
}
|
}
|
||||||
_ExecuteMainThreadRunLoopSources();
|
_ExecuteMainThreadRunLoopSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
[self stop];
|
[self stop];
|
||||||
|
|
||||||
_ExecuteMainThreadRunLoopSources();
|
_ExecuteMainThreadRunLoopSources();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ static int32_t _connectionCounter = 0;
|
|||||||
NSUInteger _bytesRead;
|
NSUInteger _bytesRead;
|
||||||
NSUInteger _bytesWritten;
|
NSUInteger _bytesWritten;
|
||||||
BOOL _virtualHEAD;
|
BOOL _virtualHEAD;
|
||||||
|
|
||||||
CFHTTPMessageRef _requestMessage;
|
CFHTTPMessageRef _requestMessage;
|
||||||
GCDWebServerRequest* _request;
|
GCDWebServerRequest* _request;
|
||||||
GCDWebServerHandler* _handler;
|
GCDWebServerHandler* _handler;
|
||||||
CFHTTPMessageRef _responseMessage;
|
CFHTTPMessageRef _responseMessage;
|
||||||
GCDWebServerResponse* _response;
|
GCDWebServerResponse* _response;
|
||||||
NSInteger _statusCode;
|
NSInteger _statusCode;
|
||||||
|
|
||||||
BOOL _opened;
|
BOOL _opened;
|
||||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||||
NSUInteger _connectionIndex;
|
NSUInteger _connectionIndex;
|
||||||
@@ -89,7 +89,7 @@ static int32_t _connectionCounter = 0;
|
|||||||
|
|
||||||
- (void)_readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
|
- (void)_readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
|
||||||
dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
|
dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
size_t size = dispatch_data_get_size(buffer);
|
size_t size = dispatch_data_get_size(buffer);
|
||||||
@@ -114,68 +114,72 @@ static int32_t _connectionCounter = 0;
|
|||||||
block(NO);
|
block(NO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
|
- (void)_readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
|
||||||
GWS_DCHECK(_requestMessage);
|
GWS_DCHECK(_requestMessage);
|
||||||
[self _readData:headersData withLength:NSUIntegerMax completionBlock:^(BOOL success) {
|
[self _readData:headersData
|
||||||
|
withLength:NSUIntegerMax
|
||||||
if (success) {
|
completionBlock:^(BOOL success) {
|
||||||
NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
|
|
||||||
if (range.location == NSNotFound) {
|
if (success) {
|
||||||
[self _readHeaders:headersData withCompletionBlock:block];
|
NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
|
||||||
} else {
|
if (range.location == NSNotFound) {
|
||||||
NSUInteger length = range.location + range.length;
|
[self _readHeaders:headersData withCompletionBlock:block];
|
||||||
if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
|
|
||||||
if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
|
|
||||||
block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
|
|
||||||
} else {
|
} else {
|
||||||
GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
|
NSUInteger length = range.location + range.length;
|
||||||
block(nil);
|
if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
|
||||||
|
if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
|
||||||
|
block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
|
||||||
|
block(nil);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
|
||||||
|
block(nil);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
|
|
||||||
block(nil);
|
block(nil);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
}];
|
||||||
block(nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
|
- (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
|
||||||
GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
|
GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
|
||||||
NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
|
NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
|
||||||
[self _readData:bodyData withLength:length completionBlock:^(BOOL success) {
|
[self _readData:bodyData
|
||||||
|
withLength:length
|
||||||
if (success) {
|
completionBlock:^(BOOL success) {
|
||||||
if (bodyData.length <= length) {
|
|
||||||
NSError* error = nil;
|
if (success) {
|
||||||
if ([_request performWriteData:bodyData error:&error]) {
|
if (bodyData.length <= length) {
|
||||||
NSUInteger remainingLength = length - bodyData.length;
|
NSError* error = nil;
|
||||||
if (remainingLength) {
|
if ([_request performWriteData:bodyData error:&error]) {
|
||||||
[self _readBodyWithRemainingLength:remainingLength completionBlock:block];
|
NSUInteger remainingLength = length - bodyData.length;
|
||||||
|
if (remainingLength) {
|
||||||
|
[self _readBodyWithRemainingLength:remainingLength completionBlock:block];
|
||||||
|
} else {
|
||||||
|
block(YES);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
||||||
|
block(NO);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
block(YES);
|
GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket);
|
||||||
|
block(NO);
|
||||||
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
|
||||||
block(NO);
|
block(NO);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket);
|
}];
|
||||||
block(NO);
|
|
||||||
GWS_DNOT_REACHED();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
block(NO);
|
|
||||||
}
|
|
||||||
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||||
@@ -189,7 +193,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
|
|
||||||
- (void)_readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block {
|
- (void)_readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block {
|
||||||
GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]);
|
GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)];
|
NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)];
|
||||||
if (range.location == NSNotFound) {
|
if (range.location == NSNotFound) {
|
||||||
@@ -230,16 +234,18 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _readData:chunkData withLength:NSUIntegerMax completionBlock:^(BOOL success) {
|
[self _readData:chunkData
|
||||||
|
withLength:NSUIntegerMax
|
||||||
if (success) {
|
completionBlock:^(BOOL success) {
|
||||||
[self _readNextBodyChunk:chunkData completionBlock:block];
|
|
||||||
} else {
|
if (success) {
|
||||||
block(NO);
|
[self _readNextBodyChunk:chunkData completionBlock:block];
|
||||||
}
|
} else {
|
||||||
|
block(NO);
|
||||||
}];
|
}
|
||||||
|
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -251,7 +257,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
[data self]; // Keeps ARC from releasing data too early
|
[data self]; // Keeps ARC from releasing data too early
|
||||||
});
|
});
|
||||||
dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
|
dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
GWS_DCHECK(remainingData == NULL);
|
GWS_DCHECK(remainingData == NULL);
|
||||||
@@ -262,7 +268,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
block(NO);
|
block(NO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||||
dispatch_release(buffer);
|
dispatch_release(buffer);
|
||||||
@@ -279,7 +285,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
- (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
|
- (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
|
||||||
GWS_DCHECK([_response hasBody]);
|
GWS_DCHECK([_response hasBody]);
|
||||||
[_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
|
[_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
if (data.length) {
|
if (data.length) {
|
||||||
if (_response.usesChunkedTransferEncoding) {
|
if (_response.usesChunkedTransferEncoding) {
|
||||||
@@ -302,22 +308,24 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
*ptr = '\n';
|
*ptr = '\n';
|
||||||
data = chunk;
|
data = chunk;
|
||||||
}
|
}
|
||||||
[self _writeData:data withCompletionBlock:^(BOOL success) {
|
[self _writeData:data
|
||||||
|
withCompletionBlock:^(BOOL success) {
|
||||||
if (success) {
|
|
||||||
[self _writeBodyWithCompletionBlock:block];
|
if (success) {
|
||||||
} else {
|
[self _writeBodyWithCompletionBlock:block];
|
||||||
block(NO);
|
} else {
|
||||||
}
|
block(NO);
|
||||||
|
}
|
||||||
}];
|
|
||||||
|
}];
|
||||||
} else {
|
} else {
|
||||||
if (_response.usesChunkedTransferEncoding) {
|
if (_response.usesChunkedTransferEncoding) {
|
||||||
[self _writeData:_lastChunkData withCompletionBlock:^(BOOL success) {
|
[self _writeData:_lastChunkData
|
||||||
|
withCompletionBlock:^(BOOL success) {
|
||||||
block(success);
|
|
||||||
|
block(success);
|
||||||
}];
|
|
||||||
|
}];
|
||||||
} else {
|
} else {
|
||||||
block(YES);
|
block(YES);
|
||||||
}
|
}
|
||||||
@@ -326,7 +334,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
|
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
|
||||||
block(NO);
|
block(NO);
|
||||||
}
|
}
|
||||||
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +342,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
|
|
||||||
@implementation GCDWebServerConnection
|
@implementation GCDWebServerConnection
|
||||||
|
|
||||||
@synthesize server=_server, localAddressData=_localAddress, remoteAddressData=_remoteAddress, totalBytesRead=_bytesRead, totalBytesWritten=_bytesWritten;
|
@synthesize server = _server, localAddressData = _localAddress, remoteAddressData = _remoteAddress, totalBytesRead = _bytesRead, totalBytesWritten = _bytesWritten;
|
||||||
|
|
||||||
+ (void)initialize {
|
+ (void)initialize {
|
||||||
if (_CRLFData == nil) {
|
if (_CRLFData == nil) {
|
||||||
@@ -376,14 +384,15 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
|
|
||||||
- (void)_startProcessingRequest {
|
- (void)_startProcessingRequest {
|
||||||
GWS_DCHECK(_responseMessage == NULL);
|
GWS_DCHECK(_responseMessage == NULL);
|
||||||
|
|
||||||
GCDWebServerResponse* preflightResponse = [self preflightRequest:_request];
|
GCDWebServerResponse* preflightResponse = [self preflightRequest:_request];
|
||||||
if (preflightResponse) {
|
if (preflightResponse) {
|
||||||
[self _finishProcessingRequest:preflightResponse];
|
[self _finishProcessingRequest:preflightResponse];
|
||||||
} else {
|
} else {
|
||||||
[self processRequest:_request completion:^(GCDWebServerResponse* processResponse) {
|
[self processRequest:_request
|
||||||
[self _finishProcessingRequest:processResponse];
|
completion:^(GCDWebServerResponse* processResponse) {
|
||||||
}];
|
[self _finishProcessingRequest:processResponse];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,7 +400,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
- (void)_finishProcessingRequest:(GCDWebServerResponse*)response {
|
- (void)_finishProcessingRequest:(GCDWebServerResponse*)response {
|
||||||
GWS_DCHECK(_responseMessage == NULL);
|
GWS_DCHECK(_responseMessage == NULL);
|
||||||
BOOL hasBody = NO;
|
BOOL hasBody = NO;
|
||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
response = [self overrideResponse:response forRequest:_request];
|
response = [self overrideResponse:response forRequest:_request];
|
||||||
}
|
}
|
||||||
@@ -407,7 +416,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
_response = response;
|
_response = response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_response) {
|
if (_response) {
|
||||||
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
||||||
if (_response.lastModifiedDate) {
|
if (_response.lastModifiedDate) {
|
||||||
@@ -436,24 +445,23 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
||||||
}];
|
}];
|
||||||
[self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
[self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
if (hasBody) {
|
if (hasBody) {
|
||||||
[self _writeBodyWithCompletionBlock:^(BOOL successInner) {
|
[self _writeBodyWithCompletionBlock:^(BOOL successInner) {
|
||||||
|
|
||||||
[_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
|
[_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
|
||||||
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
} else if (hasBody) {
|
} else if (hasBody) {
|
||||||
[_response performClose];
|
[_response performClose];
|
||||||
}
|
}
|
||||||
|
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData {
|
- (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData {
|
||||||
@@ -463,7 +471,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialData.length) {
|
if (initialData.length) {
|
||||||
if (![_request performWriteData:initialData error:&error]) {
|
if (![_request performWriteData:initialData error:&error]) {
|
||||||
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
||||||
@@ -475,19 +483,20 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
}
|
}
|
||||||
length -= initialData.length;
|
length -= initialData.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length) {
|
if (length) {
|
||||||
[self _readBodyWithRemainingLength:length completionBlock:^(BOOL success) {
|
[self _readBodyWithRemainingLength:length
|
||||||
|
completionBlock:^(BOOL success) {
|
||||||
NSError* localError = nil;
|
|
||||||
if ([_request performClose:&localError]) {
|
NSError* localError = nil;
|
||||||
[self _startProcessingRequest];
|
if ([_request performClose:&localError]) {
|
||||||
} else {
|
[self _startProcessingRequest];
|
||||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
} else {
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||||
}
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
|
}
|
||||||
}];
|
|
||||||
|
}];
|
||||||
} else {
|
} else {
|
||||||
if ([_request performClose:&error]) {
|
if ([_request performClose:&error]) {
|
||||||
[self _startProcessingRequest];
|
[self _startProcessingRequest];
|
||||||
@@ -505,100 +514,103 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
|
NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
|
||||||
[self _readNextBodyChunk:chunkData completionBlock:^(BOOL success) {
|
[self _readNextBodyChunk:chunkData
|
||||||
|
completionBlock:^(BOOL success) {
|
||||||
NSError* localError = nil;
|
|
||||||
if ([_request performClose:&localError]) {
|
NSError* localError = nil;
|
||||||
[self _startProcessingRequest];
|
if ([_request performClose:&localError]) {
|
||||||
} else {
|
[self _startProcessingRequest];
|
||||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
} else {
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||||
}
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
|
}
|
||||||
}];
|
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_readRequestHeaders {
|
- (void)_readRequestHeaders {
|
||||||
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
||||||
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
|
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
|
||||||
[self _readHeaders:headersData withCompletionBlock:^(NSData* extraData) {
|
[self _readHeaders:headersData
|
||||||
|
withCompletionBlock:^(NSData* extraData) {
|
||||||
if (extraData) {
|
|
||||||
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase
|
if (extraData) {
|
||||||
if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
|
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase
|
||||||
requestMethod = @"GET";
|
if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
|
||||||
_virtualHEAD = YES;
|
requestMethod = @"GET";
|
||||||
}
|
_virtualHEAD = YES;
|
||||||
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(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 = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
|
|
||||||
if (_request) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
|
||||||
if (_request) {
|
NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage));
|
||||||
_request.localAddressData = self.localAddressData;
|
if (requestURL) {
|
||||||
_request.remoteAddressData = self.remoteAddressData;
|
requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
|
||||||
if ([_request hasBody]) {
|
GWS_DCHECK(requestURL);
|
||||||
[_request prepareForWriting];
|
}
|
||||||
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
|
NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL))) : nil; // Don't use -[NSURL path] which strips the ending slash
|
||||||
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
|
NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
|
||||||
if (expectHeader) {
|
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
|
||||||
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
|
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
|
||||||
[self _writeData:_continueData withCompletionBlock:^(BOOL success) {
|
for (_handler in _server.handlers) {
|
||||||
|
_request = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
|
||||||
if (success) {
|
if (_request) {
|
||||||
if (_request.usesChunkedTransferEncoding) {
|
break;
|
||||||
[self _readChunkedBodyWithInitialData:extraData];
|
}
|
||||||
} else {
|
}
|
||||||
[self _readBodyWithLength:_request.contentLength initialData:extraData];
|
if (_request) {
|
||||||
}
|
_request.localAddressData = self.localAddressData;
|
||||||
|
_request.remoteAddressData = self.remoteAddressData;
|
||||||
|
if ([_request hasBody]) {
|
||||||
|
[_request prepareForWriting];
|
||||||
|
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
|
||||||
|
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
|
||||||
|
if (expectHeader) {
|
||||||
|
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
|
||||||
|
[self _writeData:_continueData
|
||||||
|
withCompletionBlock:^(BOOL success) {
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
if (_request.usesChunkedTransferEncoding) {
|
||||||
|
[self _readChunkedBodyWithInitialData:extraData];
|
||||||
|
} else {
|
||||||
|
[self _readBodyWithLength:_request.contentLength initialData:extraData];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket);
|
||||||
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
}];
|
if (_request.usesChunkedTransferEncoding) {
|
||||||
|
[self _readChunkedBodyWithInitialData:extraData];
|
||||||
|
} else {
|
||||||
|
[self _readBodyWithLength:_request.contentLength initialData:extraData];
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket);
|
GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket);
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_request.usesChunkedTransferEncoding) {
|
[self _startProcessingRequest];
|
||||||
[self _readChunkedBodyWithInitialData:extraData];
|
|
||||||
} else {
|
|
||||||
[self _readBodyWithLength:_request.contentLength initialData:extraData];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket);
|
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
|
GWS_DCHECK(_request);
|
||||||
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_MethodNotAllowed];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
[self _startProcessingRequest];
|
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
|
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
GWS_DCHECK(_request);
|
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_MethodNotAllowed];
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
}];
|
||||||
GWS_DNOT_REACHED();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
|
||||||
}
|
|
||||||
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
|
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
|
||||||
@@ -608,15 +620,15 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
_remoteAddress = remoteAddress;
|
_remoteAddress = remoteAddress;
|
||||||
_socket = socket;
|
_socket = socket;
|
||||||
GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
||||||
|
|
||||||
[_server willStartConnection:self];
|
[_server willStartConnection:self];
|
||||||
|
|
||||||
if (![self open]) {
|
if (![self open]) {
|
||||||
close(_socket);
|
close(_socket);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
_opened = YES;
|
_opened = YES;
|
||||||
|
|
||||||
[self _readRequestHeaders];
|
[self _readRequestHeaders];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -637,17 +649,17 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
} else {
|
} else {
|
||||||
GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket);
|
GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_opened) {
|
if (_opened) {
|
||||||
[self close];
|
[self close];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_server didEndConnection:self];
|
[_server didEndConnection:self];
|
||||||
|
|
||||||
if (_requestMessage) {
|
if (_requestMessage) {
|
||||||
CFRelease(_requestMessage);
|
CFRelease(_requestMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_responseMessage) {
|
if (_responseMessage) {
|
||||||
CFRelease(_responseMessage);
|
CFRelease(_responseMessage);
|
||||||
}
|
}
|
||||||
@@ -661,24 +673,24 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||||
if (_server.recordingEnabled) {
|
if (_server.recordingEnabled) {
|
||||||
_connectionIndex = OSAtomicIncrement32(&_connectionCounter);
|
_connectionIndex = OSAtomicIncrement32(&_connectionCounter);
|
||||||
|
|
||||||
_requestPath = [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);
|
_requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||||
GWS_DCHECK(_requestFD > 0);
|
GWS_DCHECK(_requestFD > 0);
|
||||||
|
|
||||||
_responsePath = [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);
|
_responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||||
GWS_DCHECK(_responseFD > 0);
|
GWS_DCHECK(_responseFD > 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
|
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
|
||||||
GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket);
|
GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket);
|
||||||
_bytesRead += length;
|
_bytesRead += length;
|
||||||
|
|
||||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||||
if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
|
if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
|
||||||
GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
|
GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
|
||||||
@@ -691,7 +703,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
|
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
|
||||||
GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket);
|
GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket);
|
||||||
_bytesWritten += length;
|
_bytesWritten += length;
|
||||||
|
|
||||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||||
if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
|
if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
|
||||||
GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
|
GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
|
||||||
@@ -819,7 +831,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
|
|||||||
}
|
}
|
||||||
unlink([_requestPath fileSystemRepresentation]);
|
unlink([_requestPath fileSystemRepresentation]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_responsePath) {
|
if (_responsePath) {
|
||||||
BOOL success = NO;
|
BOOL success = NO;
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
@@ -835,7 +847,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
|
|||||||
unlink([_responsePath fileSystemRepresentation]);
|
unlink([_responsePath fileSystemRepresentation]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_request) {
|
if (_request) {
|
||||||
GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
|
GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
|||||||
static NSDictionary* _overrides = nil;
|
static NSDictionary* _overrides = nil;
|
||||||
if (_overrides == nil) {
|
if (_overrides == nil) {
|
||||||
_overrides = [[NSDictionary alloc] initWithObjectsAndKeys:
|
_overrides = [[NSDictionary alloc] initWithObjectsAndKeys:
|
||||||
@"text/css", @"css",
|
@"text/css", @"css",
|
||||||
nil];
|
nil];
|
||||||
}
|
}
|
||||||
NSString* mimeType = nil;
|
NSString* mimeType = nil;
|
||||||
extension = [extension lowercaseString];
|
extension = [extension lowercaseString];
|
||||||
@@ -205,13 +205,13 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
||||||
|
|
||||||
NSString* value = nil;
|
NSString* value = nil;
|
||||||
[scanner scanUpToString:@"&" intoString:&value];
|
[scanner scanUpToString:@"&" intoString:&value];
|
||||||
if (value == nil) {
|
if (value == nil) {
|
||||||
value = @"";
|
value = @"";
|
||||||
}
|
}
|
||||||
|
|
||||||
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||||
NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil;
|
NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil;
|
||||||
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||||
@@ -222,7 +222,7 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
|||||||
GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value);
|
GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value);
|
||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([scanner isAtEnd]) {
|
if ([scanner isAtEnd]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -267,9 +267,9 @@ NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
|||||||
struct ifaddrs* list;
|
struct ifaddrs* list;
|
||||||
if (getifaddrs(&list) >= 0) {
|
if (getifaddrs(&list) >= 0) {
|
||||||
for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) {
|
for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) {
|
||||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV
|
#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
|
// 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
|
// Assumption holds for Apple TV running tvOS
|
||||||
if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1"))
|
if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1"))
|
||||||
#else
|
#else
|
||||||
if (strcmp(ifap->ifa_name, primaryInterface))
|
if (strcmp(ifap->ifa_name, primaryInterface))
|
||||||
|
|||||||
@@ -123,14 +123,29 @@ extern GCDWebServerLoggingLevel GCDWebServerLogLevel;
|
|||||||
extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3);
|
extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3);
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
#define GWS_LOG_DEBUG(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); } while (0)
|
#define GWS_LOG_DEBUG(...) \
|
||||||
|
do { \
|
||||||
|
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
#else
|
#else
|
||||||
#define GWS_LOG_DEBUG(...)
|
#define GWS_LOG_DEBUG(...)
|
||||||
#endif
|
#endif
|
||||||
#define GWS_LOG_VERBOSE(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); } while (0)
|
#define GWS_LOG_VERBOSE(...) \
|
||||||
#define GWS_LOG_INFO(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); } while (0)
|
do { \
|
||||||
#define GWS_LOG_WARNING(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); } while (0)
|
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \
|
||||||
#define GWS_LOG_ERROR(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); } while (0)
|
} while (0)
|
||||||
|
#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)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -143,10 +158,10 @@ extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* for
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
||||||
#define GWS_DCHECK(__CONDITION__) \
|
#define GWS_DCHECK(__CONDITION__) \
|
||||||
do { \
|
do { \
|
||||||
if (!(__CONDITION__)) { \
|
if (!(__CONDITION__)) { \
|
||||||
abort(); \
|
abort(); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#define GWS_DNOT_REACHED() abort()
|
#define GWS_DNOT_REACHED() abort()
|
||||||
|
|
||||||
@@ -171,7 +186,7 @@ static inline BOOL GCDWebServerIsValidByteRange(NSRange range) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline NSError* GCDWebServerMakePosixError(int code) {
|
static inline NSError* GCDWebServerMakePosixError(int code) {
|
||||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:strerror(code)]}];
|
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithUTF8String:strerror(code)]}];
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void GCDWebServerInitializeFunctions();
|
extern void GCDWebServerInitializeFunctions();
|
||||||
@@ -181,7 +196,7 @@ extern NSString* GCDWebServerExtractHeaderValueParameter(NSString* header, NSStr
|
|||||||
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
|
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
|
||||||
extern BOOL GCDWebServerIsTextContentType(NSString* type);
|
extern BOOL GCDWebServerIsTextContentType(NSString* type);
|
||||||
extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
|
extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
|
||||||
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1,2);
|
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
|
||||||
extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService);
|
extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService);
|
||||||
|
|
||||||
@interface GCDWebServerConnection ()
|
@interface GCDWebServerConnection ()
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
BOOL _gzipAccepted;
|
BOOL _gzipAccepted;
|
||||||
NSData* _localAddress;
|
NSData* _localAddress;
|
||||||
NSData* _remoteAddress;
|
NSData* _remoteAddress;
|
||||||
|
|
||||||
BOOL _opened;
|
BOOL _opened;
|
||||||
NSMutableArray* _decoders;
|
NSMutableArray* _decoders;
|
||||||
NSMutableDictionary* _attributes;
|
NSMutableDictionary* _attributes;
|
||||||
@@ -169,8 +169,8 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
|
|
||||||
@implementation GCDWebServerRequest : NSObject
|
@implementation GCDWebServerRequest : NSObject
|
||||||
|
|
||||||
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, ifModifiedSince=_modifiedSince, ifNoneMatch=_noneMatch,
|
@synthesize method = _method, URL = _url, headers = _headers, path = _path, query = _query, contentType = _type, contentLength = _length, ifModifiedSince = _modifiedSince, ifNoneMatch = _noneMatch,
|
||||||
byteRange=_range, acceptsGzipContentEncoding=_gzipAccepted, usesChunkedTransferEncoding=_chunked, localAddressData=_localAddress, remoteAddressData=_remoteAddress;
|
byteRange = _range, acceptsGzipContentEncoding = _gzipAccepted, usesChunkedTransferEncoding = _chunked, localAddressData = _localAddress, remoteAddressData = _remoteAddress;
|
||||||
|
|
||||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
@@ -179,7 +179,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
_headers = headers;
|
_headers = headers;
|
||||||
_path = [path copy];
|
_path = [path copy];
|
||||||
_query = query;
|
_query = query;
|
||||||
|
|
||||||
_type = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]);
|
_type = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]);
|
||||||
_chunked = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"];
|
_chunked = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"];
|
||||||
NSString* lengthHeader = [_headers objectForKey:@"Content-Length"];
|
NSString* lengthHeader = [_headers objectForKey:@"Content-Length"];
|
||||||
@@ -206,13 +206,13 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
}
|
}
|
||||||
_length = NSUIntegerMax;
|
_length = NSUIntegerMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"];
|
NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"];
|
||||||
if (modifiedHeader) {
|
if (modifiedHeader) {
|
||||||
_modifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy];
|
_modifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy];
|
||||||
}
|
}
|
||||||
_noneMatch = [_headers objectForKey:@"If-None-Match"];
|
_noneMatch = [_headers objectForKey:@"If-None-Match"];
|
||||||
|
|
||||||
_range = NSMakeRange(NSUIntegerMax, 0);
|
_range = NSMakeRange(NSUIntegerMax, 0);
|
||||||
NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]);
|
NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]);
|
||||||
if (rangeHeader) {
|
if (rangeHeader) {
|
||||||
@@ -242,11 +242,11 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
|||||||
GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url);
|
GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) {
|
if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) {
|
||||||
_gzipAccepted = YES;
|
_gzipAccepted = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
_decoders = [[NSMutableArray alloc] init];
|
_decoders = [[NSMutableArray alloc] init];
|
||||||
_attributes = [[NSMutableDictionary alloc] init];
|
_attributes = [[NSMutableDictionary alloc] init];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,7 @@
|
|||||||
NSMutableDictionary* _headers;
|
NSMutableDictionary* _headers;
|
||||||
BOOL _chunked;
|
BOOL _chunked;
|
||||||
BOOL _gzipped;
|
BOOL _gzipped;
|
||||||
|
|
||||||
BOOL _opened;
|
BOOL _opened;
|
||||||
NSMutableArray* _encoders;
|
NSMutableArray* _encoders;
|
||||||
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
|
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
|
||||||
@@ -177,8 +177,8 @@
|
|||||||
|
|
||||||
@implementation GCDWebServerResponse
|
@implementation GCDWebServerResponse
|
||||||
|
|
||||||
@synthesize contentType=_type, contentLength=_length, statusCode=_status, cacheControlMaxAge=_maxAge, lastModifiedDate=_lastModified, eTag=_eTag,
|
@synthesize contentType = _type, contentLength = _length, statusCode = _status, cacheControlMaxAge = _maxAge, lastModifiedDate = _lastModified, eTag = _eTag,
|
||||||
gzipContentEncodingEnabled=_gzipped, additionalHeaders=_headers;
|
gzipContentEncodingEnabled = _gzipped, additionalHeaders = _headers;
|
||||||
|
|
||||||
+ (instancetype)response {
|
+ (instancetype)response {
|
||||||
return [[[self class] alloc] init];
|
return [[[self class] alloc] init];
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
@interface GCDWebServerDataRequest () {
|
@interface GCDWebServerDataRequest () {
|
||||||
@private
|
@private
|
||||||
NSMutableData* _data;
|
NSMutableData* _data;
|
||||||
|
|
||||||
NSString* _text;
|
NSString* _text;
|
||||||
id _jsonObject;
|
id _jsonObject;
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
@implementation GCDWebServerDataRequest
|
@implementation GCDWebServerDataRequest
|
||||||
|
|
||||||
@synthesize data=_data;
|
@synthesize data = _data;
|
||||||
|
|
||||||
- (BOOL)open:(NSError**)error {
|
- (BOOL)open:(NSError**)error {
|
||||||
if (self.contentLength != NSUIntegerMax) {
|
if (self.contentLength != NSUIntegerMax) {
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
}
|
}
|
||||||
if (_data == nil) {
|
if (_data == nil) {
|
||||||
if (error) {
|
if (error) {
|
||||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed allocating memory"}];
|
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed allocating memory" }];
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
@implementation GCDWebServerFileRequest
|
@implementation GCDWebServerFileRequest
|
||||||
|
|
||||||
@synthesize temporaryPath=_temporaryPath;
|
@synthesize temporaryPath = _temporaryPath;
|
||||||
|
|
||||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||||
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
||||||
@@ -85,14 +85,14 @@
|
|||||||
NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"];
|
NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"];
|
||||||
if (creationDateHeader) {
|
if (creationDateHeader) {
|
||||||
NSDate* date = GCDWebServerParseISO8601(creationDateHeader);
|
NSDate* date = GCDWebServerParseISO8601(creationDateHeader);
|
||||||
if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate: date} ofItemAtPath:_temporaryPath error:error]) {
|
if (!date || ![[NSFileManager defaultManager] setAttributes:@{ NSFileCreationDate : date } ofItemAtPath:_temporaryPath error:error]) {
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"];
|
NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"];
|
||||||
if (modifiedDateHeader) {
|
if (modifiedDateHeader) {
|
||||||
NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader);
|
NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader);
|
||||||
if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: date} ofItemAtPath:_temporaryPath error:error]) {
|
if (!date || ![[NSFileManager defaultManager] setAttributes:@{ NSFileModificationDate : date } ofItemAtPath:_temporaryPath error:error]) {
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
|
|
||||||
@implementation GCDWebServerMultiPart
|
@implementation GCDWebServerMultiPart
|
||||||
|
|
||||||
@synthesize controlName=_controlName, contentType=_contentType, mimeType=_mimeType;
|
@synthesize controlName = _controlName, contentType = _contentType, mimeType = _mimeType;
|
||||||
|
|
||||||
- (id)initWithControlName:(NSString*)name contentType:(NSString*)type {
|
- (id)initWithControlName:(NSString*)name contentType:(NSString*)type {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
@@ -83,12 +83,12 @@ static NSData* _dashNewlineData = nil;
|
|||||||
|
|
||||||
@implementation GCDWebServerMultiPartArgument
|
@implementation GCDWebServerMultiPartArgument
|
||||||
|
|
||||||
@synthesize data=_data, string=_string;
|
@synthesize data = _data, string = _string;
|
||||||
|
|
||||||
- (id)initWithControlName:(NSString*)name contentType:(NSString*)type data:(NSData*)data {
|
- (id)initWithControlName:(NSString*)name contentType:(NSString*)type data:(NSData*)data {
|
||||||
if ((self = [super initWithControlName:name contentType:type])) {
|
if ((self = [super initWithControlName:name contentType:type])) {
|
||||||
_data = data;
|
_data = data;
|
||||||
|
|
||||||
if ([self.contentType hasPrefix:@"text/"]) {
|
if ([self.contentType hasPrefix:@"text/"]) {
|
||||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||||
_string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
_string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||||
@@ -112,7 +112,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
|
|
||||||
@implementation GCDWebServerMultiPartFile
|
@implementation GCDWebServerMultiPartFile
|
||||||
|
|
||||||
@synthesize fileName=_fileName, temporaryPath=_temporaryPath;
|
@synthesize fileName = _fileName, temporaryPath = _temporaryPath;
|
||||||
|
|
||||||
- (id)initWithControlName:(NSString*)name contentType:(NSString*)type fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath {
|
- (id)initWithControlName:(NSString*)name contentType:(NSString*)type fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath {
|
||||||
if ((self = [super initWithControlName:name contentType:type])) {
|
if ((self = [super initWithControlName:name contentType:type])) {
|
||||||
@@ -140,7 +140,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
NSMutableData* _data;
|
NSMutableData* _data;
|
||||||
NSMutableArray* _arguments;
|
NSMutableArray* _arguments;
|
||||||
NSMutableArray* _files;
|
NSMutableArray* _files;
|
||||||
|
|
||||||
NSString* _controlName;
|
NSString* _controlName;
|
||||||
NSString* _fileName;
|
NSString* _fileName;
|
||||||
NSString* _contentType;
|
NSString* _contentType;
|
||||||
@@ -194,11 +194,10 @@ static NSData* _dashNewlineData = nil;
|
|||||||
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
|
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
|
||||||
- (BOOL)_parseData {
|
- (BOOL)_parseData {
|
||||||
BOOL success = YES;
|
BOOL success = YES;
|
||||||
|
|
||||||
if (_state == kParserState_Headers) {
|
if (_state == kParserState_Headers) {
|
||||||
NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)];
|
NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)];
|
||||||
if (range.location != NSNotFound) {
|
if (range.location != NSNotFound) {
|
||||||
|
|
||||||
_controlName = nil;
|
_controlName = nil;
|
||||||
_fileName = nil;
|
_fileName = nil;
|
||||||
_contentType = nil;
|
_contentType = nil;
|
||||||
@@ -256,12 +255,12 @@ static NSData* _dashNewlineData = nil;
|
|||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
success = NO;
|
success = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
[_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
|
[_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
|
||||||
_state = kParserState_Content;
|
_state = kParserState_Content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((_state == kParserState_Start) || (_state == kParserState_Content)) {
|
if ((_state == kParserState_Start) || (_state == kParserState_Content)) {
|
||||||
NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.length)];
|
NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.length)];
|
||||||
if (range.location != NSNotFound) {
|
if (range.location != NSNotFound) {
|
||||||
@@ -269,7 +268,6 @@ static NSData* _dashNewlineData = nil;
|
|||||||
NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange];
|
NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange];
|
||||||
NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange];
|
NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange];
|
||||||
if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) {
|
if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) {
|
||||||
|
|
||||||
if (_state == kParserState_Content) {
|
if (_state == kParserState_Content) {
|
||||||
const void* dataBytes = _data.bytes;
|
const void* dataBytes = _data.bytes;
|
||||||
NSUInteger dataLength = range.location - 2;
|
NSUInteger dataLength = range.location - 2;
|
||||||
@@ -301,7 +299,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
[_arguments addObject:argument];
|
[_arguments addObject:argument];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subRange1.location != NSNotFound) {
|
if (subRange1.location != NSNotFound) {
|
||||||
[_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
|
[_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
|
||||||
_state = kParserState_Headers;
|
_state = kParserState_Headers;
|
||||||
@@ -333,7 +331,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +356,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
|
|
||||||
@implementation GCDWebServerMultiPartFormRequest
|
@implementation GCDWebServerMultiPartFormRequest
|
||||||
|
|
||||||
@synthesize arguments=_arguments, files=_files;
|
@synthesize arguments = _arguments, files = _files;
|
||||||
|
|
||||||
+ (NSString*)mimeType {
|
+ (NSString*)mimeType {
|
||||||
return @"multipart/form-data";
|
return @"multipart/form-data";
|
||||||
@@ -377,7 +375,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
_parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files];
|
_parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files];
|
||||||
if (_parser == nil) {
|
if (_parser == nil) {
|
||||||
if (error) {
|
if (error) {
|
||||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed starting to parse multipart form data"}];
|
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed starting to parse multipart form data" }];
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
@@ -387,7 +385,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||||
if (![_parser appendBytes:data.bytes length:data.length]) {
|
if (![_parser appendBytes:data.bytes length:data.length]) {
|
||||||
if (error) {
|
if (error) {
|
||||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed continuing to parse multipart form data"}];
|
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed continuing to parse multipart form data" }];
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
@@ -399,7 +397,7 @@ static NSData* _dashNewlineData = nil;
|
|||||||
_parser = nil;
|
_parser = nil;
|
||||||
if (!atEnd) {
|
if (!atEnd) {
|
||||||
if (error) {
|
if (error) {
|
||||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed finishing to parse multipart form data"}];
|
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed finishing to parse multipart form data" }];
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
@implementation GCDWebServerURLEncodedFormRequest
|
@implementation GCDWebServerURLEncodedFormRequest
|
||||||
|
|
||||||
@synthesize arguments=_arguments;
|
@synthesize arguments = _arguments;
|
||||||
|
|
||||||
+ (NSString*)mimeType {
|
+ (NSString*)mimeType {
|
||||||
return @"application/x-www-form-urlencoded";
|
return @"application/x-www-form-urlencoded";
|
||||||
@@ -49,12 +49,12 @@
|
|||||||
if (![super close:error]) {
|
if (![super close:error]) {
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||||
NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||||
_arguments = GCDWebServerParseURLEncodedForm(string);
|
_arguments = GCDWebServerParseURLEncodedForm(string);
|
||||||
GWS_DCHECK(_arguments);
|
GWS_DCHECK(_arguments);
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,10 @@
|
|||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_data = data;
|
_data = data;
|
||||||
|
|
||||||
self.contentType = type;
|
self.contentType = type;
|
||||||
self.contentLength = data.length;
|
self.contentLength = data.length;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,45 +37,45 @@
|
|||||||
/**
|
/**
|
||||||
* Creates a client error response with the corresponding HTTP status code.
|
* Creates a client error response with the corresponding HTTP status code.
|
||||||
*/
|
*/
|
||||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
|
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a server error response with the corresponding HTTP status code.
|
* Creates a server error response with the corresponding HTTP status code.
|
||||||
*/
|
*/
|
||||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
|
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a client error response with the corresponding HTTP status code
|
* Creates a client error response with the corresponding HTTP status code
|
||||||
* and an underlying NSError.
|
* and an underlying NSError.
|
||||||
*/
|
*/
|
||||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4);
|
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a server error response with the corresponding HTTP status code
|
* Creates a server error response with the corresponding HTTP status code
|
||||||
* and an underlying NSError.
|
* and an underlying NSError.
|
||||||
*/
|
*/
|
||||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4);
|
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a client error response with the corresponding HTTP status code.
|
* Initializes a client error response with the corresponding HTTP status code.
|
||||||
*/
|
*/
|
||||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
|
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a server error response with the corresponding HTTP status code.
|
* Initializes a server error response with the corresponding HTTP status code.
|
||||||
*/
|
*/
|
||||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
|
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a client error response with the corresponding HTTP status code
|
* Initializes a client error response with the corresponding HTTP status code
|
||||||
* and an underlying NSError.
|
* and an underlying NSError.
|
||||||
*/
|
*/
|
||||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4);
|
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a server error response with the corresponding HTTP status code
|
* Initializes a server error response with the corresponding HTTP status code
|
||||||
* and an underlying NSError.
|
* and an underlying NSError.
|
||||||
*/
|
*/
|
||||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4);
|
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
NSUInteger fileSize = (NSUInteger)info.st_size;
|
NSUInteger fileSize = (NSUInteger)info.st_size;
|
||||||
|
|
||||||
BOOL hasByteRange = GCDWebServerIsValidByteRange(range);
|
BOOL hasByteRange = GCDWebServerIsValidByteRange(range);
|
||||||
if (hasByteRange) {
|
if (hasByteRange) {
|
||||||
if (range.location != NSUIntegerMax) {
|
if (range.location != NSUIntegerMax) {
|
||||||
@@ -108,7 +108,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
|||||||
range.location = 0;
|
range.location = 0;
|
||||||
range.length = fileSize;
|
range.length = fileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_path = [path copy];
|
_path = [path copy];
|
||||||
_offset = range.location;
|
_offset = range.location;
|
||||||
@@ -118,7 +118,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
|||||||
[self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"];
|
[self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"];
|
||||||
GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path);
|
GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attachment) {
|
if (attachment) {
|
||||||
NSString* fileName = [path lastPathComponent];
|
NSString* fileName = [path lastPathComponent];
|
||||||
NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
|
NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
|
||||||
@@ -130,7 +130,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
|||||||
GWS_DNOT_REACHED();
|
GWS_DNOT_REACHED();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension]);
|
self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension]);
|
||||||
self.contentLength = _size;
|
self.contentLength = _size;
|
||||||
self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec);
|
self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec);
|
||||||
|
|||||||
@@ -48,19 +48,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||||
return [self initWithContentType:type asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
|
return [self initWithContentType:type
|
||||||
|
asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
|
||||||
NSError* error = nil;
|
|
||||||
NSData* data = block(&error);
|
NSError* error = nil;
|
||||||
completionBlock(data, error);
|
NSData* data = block(&error);
|
||||||
|
completionBlock(data, error);
|
||||||
}];
|
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block {
|
- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_block = [block copy];
|
_block = [block copy];
|
||||||
|
|
||||||
self.contentType = type;
|
self.contentType = type;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
+127
-107
@@ -73,7 +73,7 @@
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*) _uniquePathForPath:(NSString*)path {
|
- (NSString*)_uniquePathForPath:(NSString*)path {
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||||
NSString* directory = [path stringByDeletingLastPathComponent];
|
NSString* directory = [path stringByDeletingLastPathComponent];
|
||||||
NSString* file = [path lastPathComponent];
|
NSString* file = [path lastPathComponent];
|
||||||
@@ -101,18 +101,18 @@
|
|||||||
if (!isDirectory) {
|
if (!isDirectory) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"\"%@\" is not a directory", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"\"%@\" is not a directory", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* directoryName = [absolutePath lastPathComponent];
|
NSString* directoryName = [absolutePath lastPathComponent];
|
||||||
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
|
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Listing directory name \"%@\" is not allowed", directoryName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Listing directory name \"%@\" is not allowed", directoryName];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
NSArray* contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error];
|
NSArray* contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error];
|
||||||
if (contents == nil) {
|
if (contents == nil) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableArray* array = [NSMutableArray array];
|
NSMutableArray* array = [NSMutableArray array];
|
||||||
for (NSString* item in [contents sortedArrayUsingSelector:@selector(localizedStandardCompare:)]) {
|
for (NSString* item in [contents sortedArrayUsingSelector:@selector(localizedStandardCompare:)]) {
|
||||||
if (_allowHidden || ![item hasPrefix:@"."]) {
|
if (_allowHidden || ![item hasPrefix:@"."]) {
|
||||||
@@ -120,15 +120,15 @@
|
|||||||
NSString* type = [attributes objectForKey:NSFileType];
|
NSString* type = [attributes objectForKey:NSFileType];
|
||||||
if ([type isEqualToString:NSFileTypeRegular] && [self _checkFileExtension:item]) {
|
if ([type isEqualToString:NSFileTypeRegular] && [self _checkFileExtension:item]) {
|
||||||
[array addObject:@{
|
[array addObject:@{
|
||||||
@"path": [relativePath stringByAppendingPathComponent:item],
|
@"path" : [relativePath stringByAppendingPathComponent:item],
|
||||||
@"name": item,
|
@"name" : item,
|
||||||
@"size": [attributes objectForKey:NSFileSize]
|
@"size" : [attributes objectForKey:NSFileSize]
|
||||||
}];
|
}];
|
||||||
} else if ([type isEqualToString:NSFileTypeDirectory]) {
|
} else if ([type isEqualToString:NSFileTypeDirectory]) {
|
||||||
[array addObject:@{
|
[array addObject:@{
|
||||||
@"path": [[relativePath stringByAppendingPathComponent:item] stringByAppendingString:@"/"],
|
@"path" : [[relativePath stringByAppendingPathComponent:item] stringByAppendingString:@"/"],
|
||||||
@"name": item
|
@"name" : item
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,13 +145,13 @@
|
|||||||
if (isDirectory) {
|
if (isDirectory) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"\"%@\" is a directory", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"\"%@\" is a directory", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* fileName = [absolutePath lastPathComponent];
|
NSString* fileName = [absolutePath lastPathComponent];
|
||||||
if (([fileName hasPrefix:@"."] && !_allowHidden) || ![self _checkFileExtension:fileName]) {
|
if (([fileName hasPrefix:@"."] && !_allowHidden) || ![self _checkFileExtension:fileName]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading file name \"%@\" is not allowed", fileName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading file name \"%@\" is not allowed", fileName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(webUploader:didDownloadFileAtPath: )]) {
|
if ([self.delegate respondsToSelector:@selector(webUploader:didDownloadFileAtPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate webUploader:self didDownloadFileAtPath:absolutePath];
|
[self.delegate webUploader:self didDownloadFileAtPath:absolutePath];
|
||||||
});
|
});
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
- (GCDWebServerResponse*)uploadFile:(GCDWebServerMultiPartFormRequest*)request {
|
- (GCDWebServerResponse*)uploadFile:(GCDWebServerMultiPartFormRequest*)request {
|
||||||
NSRange range = [[request.headers objectForKey:@"Accept"] rangeOfString:@"application/json" options:NSCaseInsensitiveSearch];
|
NSRange range = [[request.headers objectForKey:@"Accept"] rangeOfString:@"application/json" options:NSCaseInsensitiveSearch];
|
||||||
NSString* contentType = (range.location != NSNotFound ? @"application/json" : @"text/plain; charset=utf-8"); // Required when using iFrame transport (see https://github.com/blueimp/jQuery-File-Upload/wiki/Setup)
|
NSString* contentType = (range.location != NSNotFound ? @"application/json" : @"text/plain; charset=utf-8"); // Required when using iFrame transport (see https://github.com/blueimp/jQuery-File-Upload/wiki/Setup)
|
||||||
|
|
||||||
GCDWebServerMultiPartFile* file = [request firstFileForControlName:@"files[]"];
|
GCDWebServerMultiPartFile* file = [request firstFileForControlName:@"files[]"];
|
||||||
if ((!_allowHidden && [file.fileName hasPrefix:@"."]) || ![self _checkFileExtension:file.fileName]) {
|
if ((!_allowHidden && [file.fileName hasPrefix:@"."]) || ![self _checkFileExtension:file.fileName]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName];
|
||||||
@@ -172,16 +172,16 @@
|
|||||||
if (![self _checkSandboxedPath:absolutePath]) {
|
if (![self _checkSandboxedPath:absolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:file.temporaryPath]) {
|
if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:file.temporaryPath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file \"%@\" to \"%@\" is not permitted", file.fileName, relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file \"%@\" to \"%@\" is not permitted", file.fileName, relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
if (![[NSFileManager defaultManager] moveItemAtPath:file.temporaryPath toPath:absolutePath error:&error]) {
|
if (![[NSFileManager defaultManager] moveItemAtPath:file.temporaryPath toPath:absolutePath error:&error]) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(webUploader:didUploadFileAtPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(webUploader:didUploadFileAtPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate webUploader:self didUploadFileAtPath:absolutePath];
|
[self.delegate webUploader:self didUploadFileAtPath:absolutePath];
|
||||||
@@ -197,27 +197,27 @@
|
|||||||
if (![self _checkSandboxedPath:oldAbsolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:oldAbsolutePath isDirectory:&isDirectory]) {
|
if (![self _checkSandboxedPath:oldAbsolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:oldAbsolutePath isDirectory:&isDirectory]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", oldRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", oldRelativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* newRelativePath = [request.arguments objectForKey:@"newPath"];
|
NSString* newRelativePath = [request.arguments objectForKey:@"newPath"];
|
||||||
NSString* newAbsolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:newRelativePath]];
|
NSString* newAbsolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:newRelativePath]];
|
||||||
if (![self _checkSandboxedPath:newAbsolutePath]) {
|
if (![self _checkSandboxedPath:newAbsolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", newRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", newRelativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* itemName = [newAbsolutePath lastPathComponent];
|
NSString* itemName = [newAbsolutePath lastPathComponent];
|
||||||
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving to item name \"%@\" is not allowed", itemName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving to item name \"%@\" is not allowed", itemName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![self shouldMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath]) {
|
if (![self shouldMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving \"%@\" to \"%@\" is not permitted", oldRelativePath, newRelativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving \"%@\" to \"%@\" is not permitted", oldRelativePath, newRelativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
if (![[NSFileManager defaultManager] moveItemAtPath:oldAbsolutePath toPath:newAbsolutePath error:&error]) {
|
if (![[NSFileManager defaultManager] moveItemAtPath:oldAbsolutePath toPath:newAbsolutePath error:&error]) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving \"%@\" to \"%@\"", oldRelativePath, newRelativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving \"%@\" to \"%@\"", oldRelativePath, newRelativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(webUploader:didMoveItemFromPath:toPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(webUploader:didMoveItemFromPath:toPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate webUploader:self didMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath];
|
[self.delegate webUploader:self didMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath];
|
||||||
@@ -233,21 +233,21 @@
|
|||||||
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* itemName = [absolutePath lastPathComponent];
|
NSString* itemName = [absolutePath lastPathComponent];
|
||||||
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![self shouldDeleteItemAtPath:absolutePath]) {
|
if (![self shouldDeleteItemAtPath:absolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting \"%@\" is not permitted", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting \"%@\" is not permitted", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
if (![[NSFileManager defaultManager] removeItemAtPath:absolutePath error:&error]) {
|
if (![[NSFileManager defaultManager] removeItemAtPath:absolutePath error:&error]) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(webUploader:didDeleteItemAtPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(webUploader:didDeleteItemAtPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate webUploader:self didDeleteItemAtPath:absolutePath];
|
[self.delegate webUploader:self didDeleteItemAtPath:absolutePath];
|
||||||
@@ -262,21 +262,21 @@
|
|||||||
if (![self _checkSandboxedPath:absolutePath]) {
|
if (![self _checkSandboxedPath:absolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* directoryName = [absolutePath lastPathComponent];
|
NSString* directoryName = [absolutePath lastPathComponent];
|
||||||
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
|
if (!_allowHidden && [directoryName hasPrefix:@"."]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![self shouldCreateDirectoryAtPath:absolutePath]) {
|
if (![self shouldCreateDirectoryAtPath:absolutePath]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory \"%@\" is not permitted", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory \"%@\" is not permitted", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:NO attributes:nil error:&error]) {
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:NO attributes:nil error:&error]) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([self.delegate respondsToSelector:@selector(webUploader:didCreateDirectoryAtPath:)]) {
|
if ([self.delegate respondsToSelector:@selector(webUploader:didCreateDirectoryAtPath:)]) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.delegate webUploader:self didCreateDirectoryAtPath:absolutePath];
|
[self.delegate webUploader:self didCreateDirectoryAtPath:absolutePath];
|
||||||
@@ -289,8 +289,8 @@
|
|||||||
|
|
||||||
@implementation GCDWebUploader
|
@implementation GCDWebUploader
|
||||||
|
|
||||||
@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden,
|
@synthesize uploadDirectory = _uploadDirectory, allowedFileExtensions = _allowedExtensions, allowHiddenItems = _allowHidden,
|
||||||
title=_title, header=_header, prologue=_prologue, epilogue=_epilogue, footer=_footer;
|
title = _title, header = _header, prologue = _prologue, epilogue = _epilogue, footer = _footer;
|
||||||
|
|
||||||
@dynamic delegate;
|
@dynamic delegate;
|
||||||
|
|
||||||
@@ -302,96 +302,116 @@
|
|||||||
}
|
}
|
||||||
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
||||||
GCDWebUploader* __unsafe_unretained server = self;
|
GCDWebUploader* __unsafe_unretained server = self;
|
||||||
|
|
||||||
// Resource files
|
// Resource files
|
||||||
[self addGETHandlerForBasePath:@"/" directoryPath:[siteBundle resourcePath] indexFilename:nil cacheAge:3600 allowRangeRequests:NO];
|
[self addGETHandlerForBasePath:@"/" directoryPath:[siteBundle resourcePath] indexFilename:nil cacheAge:3600 allowRangeRequests:NO];
|
||||||
|
|
||||||
// Web page
|
// Web page
|
||||||
[self addHandlerForMethod:@"GET" path:@"/" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"GET"
|
||||||
|
path:@"/"
|
||||||
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
NSString* device = [[UIDevice currentDevice] name];
|
NSString* device = [[UIDevice currentDevice] name];
|
||||||
#else
|
#else
|
||||||
NSString* device = CFBridgingRelease(SCDynamicStoreCopyComputerName(NULL, NULL));
|
NSString* device = CFBridgingRelease(SCDynamicStoreCopyComputerName(NULL, NULL));
|
||||||
#endif
|
#endif
|
||||||
NSString* title = server.title;
|
NSString* title = server.title;
|
||||||
if (title == nil) {
|
if (title == nil) {
|
||||||
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||||
if (title == nil) {
|
if (title == nil) {
|
||||||
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
||||||
}
|
}
|
||||||
#if !TARGET_OS_IPHONE
|
#if !TARGET_OS_IPHONE
|
||||||
if (title == nil) {
|
if (title == nil) {
|
||||||
title = [[NSProcessInfo processInfo] processName];
|
title = [[NSProcessInfo processInfo] processName];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
NSString* header = server.header;
|
NSString* header = server.header;
|
||||||
if (header == nil) {
|
if (header == nil) {
|
||||||
header = title;
|
header = title;
|
||||||
}
|
}
|
||||||
NSString* prologue = server.prologue;
|
NSString* prologue = server.prologue;
|
||||||
if (prologue == nil) {
|
if (prologue == nil) {
|
||||||
prologue = [siteBundle localizedStringForKey:@"PROLOGUE" value:@"" table:nil];
|
prologue = [siteBundle localizedStringForKey:@"PROLOGUE" value:@"" table:nil];
|
||||||
}
|
}
|
||||||
NSString* epilogue = server.epilogue;
|
NSString* epilogue = server.epilogue;
|
||||||
if (epilogue == nil) {
|
if (epilogue == nil) {
|
||||||
epilogue = [siteBundle localizedStringForKey:@"EPILOGUE" value:@"" table:nil];
|
epilogue = [siteBundle localizedStringForKey:@"EPILOGUE" value:@"" table:nil];
|
||||||
}
|
}
|
||||||
NSString* footer = server.footer;
|
NSString* footer = server.footer;
|
||||||
if (footer == nil) {
|
if (footer == nil) {
|
||||||
NSString* name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
NSString* name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||||
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||||
#if !TARGET_OS_IPHONE
|
#if !TARGET_OS_IPHONE
|
||||||
if (!name && !version) {
|
if (!name && !version) {
|
||||||
name = @"OS X";
|
name = @"OS X";
|
||||||
version = [[NSProcessInfo processInfo] operatingSystemVersionString];
|
version = [[NSProcessInfo processInfo] operatingSystemVersionString];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
footer = [NSString stringWithFormat:[siteBundle localizedStringForKey:@"FOOTER_FORMAT" value:@"" table:nil], name, version];
|
footer = [NSString stringWithFormat:[siteBundle localizedStringForKey:@"FOOTER_FORMAT" value:@"" table:nil], name, version];
|
||||||
}
|
}
|
||||||
return [GCDWebServerDataResponse responseWithHTMLTemplate:[siteBundle pathForResource:@"index" ofType:@"html"]
|
return [GCDWebServerDataResponse responseWithHTMLTemplate:[siteBundle pathForResource:@"index" ofType:@"html"]
|
||||||
variables:@{
|
variables:@{
|
||||||
@"device": device,
|
@"device" : device,
|
||||||
@"title": title,
|
@"title" : title,
|
||||||
@"header": header,
|
@"header" : header,
|
||||||
@"prologue": prologue,
|
@"prologue" : prologue,
|
||||||
@"epilogue": epilogue,
|
@"epilogue" : epilogue,
|
||||||
@"footer": footer
|
@"footer" : footer
|
||||||
}];
|
}];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// File listing
|
// File listing
|
||||||
[self addHandlerForMethod:@"GET" path:@"/list" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"GET"
|
||||||
return [server listDirectory:request];
|
path:@"/list"
|
||||||
}];
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server listDirectory:request];
|
||||||
|
}];
|
||||||
|
|
||||||
// File download
|
// File download
|
||||||
[self addHandlerForMethod:@"GET" path:@"/download" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"GET"
|
||||||
return [server downloadFile:request];
|
path:@"/download"
|
||||||
}];
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server downloadFile:request];
|
||||||
|
}];
|
||||||
|
|
||||||
// File upload
|
// File upload
|
||||||
[self addHandlerForMethod:@"POST" path:@"/upload" requestClass:[GCDWebServerMultiPartFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"POST"
|
||||||
return [server uploadFile:(GCDWebServerMultiPartFormRequest*)request];
|
path:@"/upload"
|
||||||
}];
|
requestClass:[GCDWebServerMultiPartFormRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server uploadFile:(GCDWebServerMultiPartFormRequest*)request];
|
||||||
|
}];
|
||||||
|
|
||||||
// File and folder moving
|
// File and folder moving
|
||||||
[self addHandlerForMethod:@"POST" path:@"/move" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"POST"
|
||||||
return [server moveItem:(GCDWebServerURLEncodedFormRequest*)request];
|
path:@"/move"
|
||||||
}];
|
requestClass:[GCDWebServerURLEncodedFormRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server moveItem:(GCDWebServerURLEncodedFormRequest*)request];
|
||||||
|
}];
|
||||||
|
|
||||||
// File and folder deletion
|
// File and folder deletion
|
||||||
[self addHandlerForMethod:@"POST" path:@"/delete" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"POST"
|
||||||
return [server deleteItem:(GCDWebServerURLEncodedFormRequest*)request];
|
path:@"/delete"
|
||||||
}];
|
requestClass:[GCDWebServerURLEncodedFormRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server deleteItem:(GCDWebServerURLEncodedFormRequest*)request];
|
||||||
|
}];
|
||||||
|
|
||||||
// Directory creation
|
// Directory creation
|
||||||
[self addHandlerForMethod:@"POST" path:@"/create" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addHandlerForMethod:@"POST"
|
||||||
return [server createDirectory:(GCDWebServerURLEncodedFormRequest*)request];
|
path:@"/create"
|
||||||
}];
|
requestClass:[GCDWebServerURLEncodedFormRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
return [server createDirectory:(GCDWebServerURLEncodedFormRequest*)request];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|||||||
+111
-110
@@ -147,7 +147,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
NSString* authenticationPassword = nil;
|
NSString* authenticationPassword = nil;
|
||||||
BOOL bindToLocalhost = NO;
|
BOOL bindToLocalhost = NO;
|
||||||
BOOL requestNATPortMapping = NO;
|
BOOL requestNATPortMapping = NO;
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse | asyncResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password] [--localhost]\n\n", basename((char*)argv[0]));
|
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse | asyncResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password] [--localhost]\n\n", basename((char*)argv[0]));
|
||||||
} else {
|
} else {
|
||||||
@@ -201,10 +201,9 @@ int main(int argc, const char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GCDWebServer* webServer = nil;
|
GCDWebServer* webServer = nil;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
|
||||||
// Simply serve contents of home directory
|
// Simply serve contents of home directory
|
||||||
case kMode_WebServer: {
|
case kMode_WebServer: {
|
||||||
fprintf(stdout, "Running in Web Server mode from \"%s\"", [rootDirectory UTF8String]);
|
fprintf(stdout, "Running in Web Server mode from \"%s\"", [rootDirectory UTF8String]);
|
||||||
@@ -212,21 +211,21 @@ int main(int argc, const char* argv[]) {
|
|||||||
[webServer addGETHandlerForBasePath:@"/" directoryPath:rootDirectory indexFilename:nil cacheAge:0 allowRangeRequests:YES];
|
[webServer addGETHandlerForBasePath:@"/" directoryPath:rootDirectory indexFilename:nil cacheAge:0 allowRangeRequests:YES];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renders a HTML page
|
// Renders a HTML page
|
||||||
case kMode_HTMLPage: {
|
case kMode_HTMLPage: {
|
||||||
fprintf(stdout, "Running in HTML Page mode");
|
fprintf(stdout, "Running in HTML Page mode");
|
||||||
webServer = [[GCDWebServer alloc] init];
|
webServer = [[GCDWebServer alloc] init];
|
||||||
[webServer addDefaultHandlerForMethod:@"GET"
|
[webServer addDefaultHandlerForMethod:@"GET"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
return [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
|
return [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements an HTML form
|
// Implements an HTML form
|
||||||
case kMode_HTMLForm: {
|
case kMode_HTMLForm: {
|
||||||
fprintf(stdout, "Running in HTML Form mode");
|
fprintf(stdout, "Running in HTML Form mode");
|
||||||
@@ -234,9 +233,9 @@ int main(int argc, const char* argv[]) {
|
|||||||
[webServer addHandlerForMethod:@"GET"
|
[webServer addHandlerForMethod:@"GET"
|
||||||
path:@"/"
|
path:@"/"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
NSString* html = @" \
|
NSString* html = @" \
|
||||||
<html><body> \
|
<html><body> \
|
||||||
<form name=\"input\" action=\"/\" method=\"post\" enctype=\"application/x-www-form-urlencoded\"> \
|
<form name=\"input\" action=\"/\" method=\"post\" enctype=\"application/x-www-form-urlencoded\"> \
|
||||||
Value: <input type=\"text\" name=\"value\"> \
|
Value: <input type=\"text\" name=\"value\"> \
|
||||||
@@ -244,22 +243,22 @@ int main(int argc, const char* argv[]) {
|
|||||||
</form> \
|
</form> \
|
||||||
</body></html> \
|
</body></html> \
|
||||||
";
|
";
|
||||||
return [GCDWebServerDataResponse responseWithHTML:html];
|
return [GCDWebServerDataResponse responseWithHTML:html];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
[webServer addHandlerForMethod:@"POST"
|
[webServer addHandlerForMethod:@"POST"
|
||||||
path:@"/"
|
path:@"/"
|
||||||
requestClass:[GCDWebServerURLEncodedFormRequest class]
|
requestClass:[GCDWebServerURLEncodedFormRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"];
|
NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"];
|
||||||
NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p></body></html>", value];
|
NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p></body></html>", value];
|
||||||
return [GCDWebServerDataResponse responseWithHTML:html];
|
return [GCDWebServerDataResponse responseWithHTML:html];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements HTML file upload
|
// Implements HTML file upload
|
||||||
case kMode_HTMLFileUpload: {
|
case kMode_HTMLFileUpload: {
|
||||||
fprintf(stdout, "Running in HTML File Upload mode");
|
fprintf(stdout, "Running in HTML File Upload mode");
|
||||||
@@ -274,48 +273,48 @@ int main(int argc, const char* argv[]) {
|
|||||||
[webServer addHandlerForMethod:@"GET"
|
[webServer addHandlerForMethod:@"GET"
|
||||||
path:@"/"
|
path:@"/"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
NSString* html = [NSString stringWithFormat:@"<html><body>%@</body></html>", formHTML];
|
NSString* html = [NSString stringWithFormat:@"<html><body>%@</body></html>", formHTML];
|
||||||
return [GCDWebServerDataResponse responseWithHTML:html];
|
return [GCDWebServerDataResponse responseWithHTML:html];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
[webServer addHandlerForMethod:@"POST"
|
[webServer addHandlerForMethod:@"POST"
|
||||||
path:@"/"
|
path:@"/"
|
||||||
requestClass:[GCDWebServerMultiPartFormRequest class]
|
requestClass:[GCDWebServerMultiPartFormRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
NSMutableString* string = [NSMutableString string];
|
NSMutableString* string = [NSMutableString string];
|
||||||
for (GCDWebServerMultiPartArgument* argument in [(GCDWebServerMultiPartFormRequest*)request arguments]) {
|
for (GCDWebServerMultiPartArgument* argument in [(GCDWebServerMultiPartFormRequest*)request arguments]) {
|
||||||
[string appendFormat:@"%@ = %@<br>", argument.controlName, argument.string];
|
[string appendFormat:@"%@ = %@<br>", argument.controlName, argument.string];
|
||||||
}
|
}
|
||||||
for (GCDWebServerMultiPartFile* file in [(GCDWebServerMultiPartFormRequest*)request files]) {
|
for (GCDWebServerMultiPartFile* file in [(GCDWebServerMultiPartFormRequest*)request files]) {
|
||||||
NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:file.temporaryPath error:NULL];
|
NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:file.temporaryPath error:NULL];
|
||||||
[string appendFormat:@"%@ = "%@" (%@ | %llu %@)<br>", file.controlName, file.fileName, file.mimeType,
|
[string appendFormat:@"%@ = "%@" (%@ | %llu %@)<br>", file.controlName, file.fileName, file.mimeType,
|
||||||
attributes.fileSize >= 1000 ? attributes.fileSize / 1000 : attributes.fileSize,
|
attributes.fileSize >= 1000 ? attributes.fileSize / 1000 : attributes.fileSize,
|
||||||
attributes.fileSize >= 1000 ? @"KB" : @"Bytes"];
|
attributes.fileSize >= 1000 ? @"KB" : @"Bytes"];
|
||||||
};
|
};
|
||||||
NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p><hr>%@</body></html>", string, formHTML];
|
NSString* html = [NSString stringWithFormat:@"<html><body><p>%@</p><hr>%@</body></html>", string, formHTML];
|
||||||
return [GCDWebServerDataResponse responseWithHTML:html];
|
return [GCDWebServerDataResponse responseWithHTML:html];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve home directory through WebDAV
|
// Serve home directory through WebDAV
|
||||||
case kMode_WebDAV: {
|
case kMode_WebDAV: {
|
||||||
fprintf(stdout, "Running in WebDAV mode from \"%s\"", [rootDirectory UTF8String]);
|
fprintf(stdout, "Running in WebDAV mode from \"%s\"", [rootDirectory UTF8String]);
|
||||||
webServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:rootDirectory];
|
webServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:rootDirectory];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve home directory through web uploader
|
// Serve home directory through web uploader
|
||||||
case kMode_WebUploader: {
|
case kMode_WebUploader: {
|
||||||
fprintf(stdout, "Running in Web Uploader mode from \"%s\"", [rootDirectory UTF8String]);
|
fprintf(stdout, "Running in Web Uploader mode from \"%s\"", [rootDirectory UTF8String]);
|
||||||
webServer = [[GCDWebUploader alloc] initWithUploadDirectory:rootDirectory];
|
webServer = [[GCDWebUploader alloc] initWithUploadDirectory:rootDirectory];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test streaming responses
|
// Test streaming responses
|
||||||
case kMode_StreamingResponse: {
|
case kMode_StreamingResponse: {
|
||||||
fprintf(stdout, "Running in Streaming Response mode");
|
fprintf(stdout, "Running in Streaming Response mode");
|
||||||
@@ -323,42 +322,44 @@ int main(int argc, const char* argv[]) {
|
|||||||
[webServer addHandlerForMethod:@"GET"
|
[webServer addHandlerForMethod:@"GET"
|
||||||
path:@"/sync"
|
path:@"/sync"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
__block int countDown = 10;
|
__block int countDown = 10;
|
||||||
return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" streamBlock:^NSData *(NSError** error) {
|
return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain"
|
||||||
|
streamBlock:^NSData*(NSError** error) {
|
||||||
usleep(100 * 1000);
|
|
||||||
if (countDown) {
|
usleep(100 * 1000);
|
||||||
return [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding];
|
if (countDown) {
|
||||||
} else {
|
return [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
return [NSData data];
|
} else {
|
||||||
}
|
return [NSData data];
|
||||||
|
}
|
||||||
}];
|
|
||||||
|
}];
|
||||||
}];
|
|
||||||
|
}];
|
||||||
[webServer addHandlerForMethod:@"GET"
|
[webServer addHandlerForMethod:@"GET"
|
||||||
path:@"/async"
|
path:@"/async"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
__block int countDown = 10;
|
__block int countDown = 10;
|
||||||
return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
|
return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain"
|
||||||
|
asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data];
|
|
||||||
completionBlock(data, nil);
|
NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data];
|
||||||
|
completionBlock(data, nil);
|
||||||
});
|
|
||||||
|
});
|
||||||
}];
|
|
||||||
|
}];
|
||||||
}];
|
|
||||||
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test async responses
|
// Test async responses
|
||||||
case kMode_AsyncResponse: {
|
case kMode_AsyncResponse: {
|
||||||
fprintf(stdout, "Running in Async Response mode");
|
fprintf(stdout, "Running in Async Response mode");
|
||||||
@@ -367,41 +368,41 @@ int main(int argc, const char* argv[]) {
|
|||||||
path:@"/async"
|
path:@"/async"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding] contentType:@"text/plain"];
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding] contentType:@"text/plain"];
|
||||||
completionBlock(response);
|
completionBlock(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
}];
|
}];
|
||||||
[webServer addHandlerForMethod:@"GET"
|
[webServer addHandlerForMethod:@"GET"
|
||||||
path:@"/async2"
|
path:@"/async2"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock handlerCompletionBlock) {
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock handlerCompletionBlock) {
|
||||||
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
|
||||||
__block int countDown = 10;
|
__block int countDown = 10;
|
||||||
GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock readerCompletionBlock) {
|
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(), ^{
|
|
||||||
|
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);
|
NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data];
|
||||||
|
readerCompletionBlock(data, nil);
|
||||||
});
|
|
||||||
|
});
|
||||||
}];
|
|
||||||
handlerCompletionBlock(response);
|
}];
|
||||||
|
handlerCompletionBlock(response);
|
||||||
});
|
|
||||||
|
});
|
||||||
}];
|
|
||||||
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (webServer) {
|
if (webServer) {
|
||||||
Delegate* delegate = [[Delegate alloc] init];
|
Delegate* delegate = [[Delegate alloc] init];
|
||||||
if (testDirectory) {
|
if (testDirectory) {
|
||||||
@@ -409,7 +410,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
webServer.delegate = delegate;
|
webServer.delegate = delegate;
|
||||||
#endif
|
#endif
|
||||||
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
|
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
|
||||||
result = (int)[webServer runTestsWithOptions:@{GCDWebServerOption_Port: @8080} inDirectory:testDirectory];
|
result = (int)[webServer runTestsWithOptions:@{ GCDWebServerOption_Port : @8080 } inDirectory:testDirectory];
|
||||||
} else {
|
} else {
|
||||||
webServer.delegate = delegate;
|
webServer.delegate = delegate;
|
||||||
if (recording) {
|
if (recording) {
|
||||||
@@ -424,7 +425,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
||||||
if (authenticationUser && authenticationPassword) {
|
if (authenticationUser && authenticationPassword) {
|
||||||
[options setValue:authenticationRealm forKey:GCDWebServerOption_AuthenticationRealm];
|
[options setValue:authenticationRealm forKey:GCDWebServerOption_AuthenticationRealm];
|
||||||
[options setObject:@{authenticationUser: authenticationPassword} forKey:GCDWebServerOption_AuthenticationAccounts];
|
[options setObject:@{ authenticationUser : authenticationPassword } forKey:GCDWebServerOption_AuthenticationAccounts];
|
||||||
if ([authenticationMethod isEqualToString:@"Basic"]) {
|
if ([authenticationMethod isEqualToString:@"Basic"]) {
|
||||||
[options setObject:GCDWebServerAuthenticationMethod_Basic forKey:GCDWebServerOption_AuthenticationMethod];
|
[options setObject:GCDWebServerAuthenticationMethod_Basic forKey:GCDWebServerOption_AuthenticationMethod];
|
||||||
} else if ([authenticationMethod isEqualToString:@"Digest"]) {
|
} else if ([authenticationMethod isEqualToString:@"Digest"]) {
|
||||||
|
|||||||
Executable
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/sh -ex
|
||||||
|
|
||||||
|
# brew install clang-format
|
||||||
|
|
||||||
|
CLANG_FORMAT_VERSION=`clang-format -version | awk '{ print $3 }'`
|
||||||
|
if [[ "$CLANG_FORMAT_VERSION" != "4.0.0" ]]; then
|
||||||
|
echo "Unsupported clang-format version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
pushd "GCDWebServer/Core"
|
||||||
|
clang-format -style=file -i *.h *.m
|
||||||
|
popd
|
||||||
|
pushd "GCDWebServer/Requests"
|
||||||
|
clang-format -style=file -i *.h *.m
|
||||||
|
popd
|
||||||
|
pushd "GCDWebServer/Responses"
|
||||||
|
clang-format -style=file -i *.h *.m
|
||||||
|
popd
|
||||||
|
pushd "GCDWebUploader"
|
||||||
|
clang-format -style=file -i *.h *.m
|
||||||
|
popd
|
||||||
|
pushd "GCDWebDAVServer"
|
||||||
|
clang-format -style=file -i *.h *.m
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd "Frameworks"
|
||||||
|
clang-format -style=file -i *.h *.m
|
||||||
|
popd
|
||||||
|
pushd "Mac"
|
||||||
|
clang-format -style=file -i *.m
|
||||||
|
popd
|
||||||
|
pushd "iOS"
|
||||||
|
clang-format -style=file -i *.h *.m
|
||||||
|
popd
|
||||||
|
pushd "tvOS"
|
||||||
|
clang-format -style=file -i *.h *.m
|
||||||
|
popd
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
- (void)viewWillAppear:(BOOL)animated {
|
||||||
[super viewWillAppear:animated];
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
||||||
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
|
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
|
||||||
_webServer.delegate = self;
|
_webServer.delegate = self;
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
- (void)viewDidDisappear:(BOOL)animated {
|
- (void)viewDidDisappear:(BOOL)animated {
|
||||||
[super viewDidDisappear:animated];
|
[super viewDidDisappear:animated];
|
||||||
|
|
||||||
[_webServer stop];
|
[_webServer stop];
|
||||||
_webServer = nil;
|
_webServer = nil;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
- (void)viewWillAppear:(BOOL)animated {
|
||||||
[super viewWillAppear:animated];
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
||||||
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
|
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath];
|
||||||
_webServer.delegate = self;
|
_webServer.delegate = self;
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
- (void)viewDidDisappear:(BOOL)animated {
|
- (void)viewDidDisappear:(BOOL)animated {
|
||||||
[super viewDidDisappear:animated];
|
[super viewDidDisappear:animated];
|
||||||
|
|
||||||
[_webServer stop];
|
[_webServer stop];
|
||||||
_webServer = nil;
|
_webServer = nil;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user