Added support for Basic Authentication
This commit is contained in:
@ -46,6 +46,8 @@ extern NSString* const GCDWebServerOption_Port; // NSNumber / NSUInteger (defau
|
|||||||
extern NSString* const GCDWebServerOption_BonjourName; // NSString (default is empty string i.e. use computer name)
|
extern NSString* const GCDWebServerOption_BonjourName; // NSString (default is empty string i.e. use computer name)
|
||||||
extern NSString* const GCDWebServerOption_MaxPendingConnections; // NSNumber / NSUInteger (default is 16)
|
extern NSString* const GCDWebServerOption_MaxPendingConnections; // NSNumber / NSUInteger (default is 16)
|
||||||
extern NSString* const GCDWebServerOption_ServerName; // NSString (default is server class name)
|
extern NSString* const GCDWebServerOption_ServerName; // NSString (default is server class name)
|
||||||
|
extern NSString* const GCDWebServerOption_AuthenticationRealm; // NSString (default is server name)
|
||||||
|
extern NSString* const GCDWebServerOption_BasicAuthenticationAccount; // NSString in the form "user:password" (default is nil i.e. basic authentication disabled)
|
||||||
extern NSString* const GCDWebServerOption_ConnectionClass; // Subclass of GCDWebServerConnection (default is GCDWebServerConnection class)
|
extern NSString* const GCDWebServerOption_ConnectionClass; // Subclass of GCDWebServerConnection (default is GCDWebServerConnection class)
|
||||||
extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET; // NSNumber / BOOL (default is YES)
|
extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET; // NSNumber / BOOL (default is YES)
|
||||||
extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval; // NSNumber / double (default is 1.0 seconds - set to <=0.0 to disable coaslescing of -webServerDidConnect: / -webServerDidDisconnect:)
|
extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval; // NSNumber / double (default is 1.0 seconds - set to <=0.0 to disable coaslescing of -webServerDidConnect: / -webServerDidDisconnect:)
|
||||||
|
|||||||
@ -54,6 +54,8 @@
|
|||||||
|
|
||||||
NSDictionary* _options;
|
NSDictionary* _options;
|
||||||
NSString* _serverName;
|
NSString* _serverName;
|
||||||
|
NSString* _authenticationRealm;
|
||||||
|
NSString* _authenticationBasicAccount;
|
||||||
Class _connectionClass;
|
Class _connectionClass;
|
||||||
BOOL _mapHEADToGET;
|
BOOL _mapHEADToGET;
|
||||||
CFTimeInterval _disconnectDelay;
|
CFTimeInterval _disconnectDelay;
|
||||||
@ -81,6 +83,8 @@ NSString* const GCDWebServerOption_Port = @"Port";
|
|||||||
NSString* const GCDWebServerOption_BonjourName = @"BonjourName";
|
NSString* const GCDWebServerOption_BonjourName = @"BonjourName";
|
||||||
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
|
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
|
||||||
NSString* const GCDWebServerOption_ServerName = @"ServerName";
|
NSString* const GCDWebServerOption_ServerName = @"ServerName";
|
||||||
|
NSString* const GCDWebServerOption_AuthenticationRealm = @"AuthenticationRealm";
|
||||||
|
NSString* const GCDWebServerOption_BasicAuthenticationAccount = @"BasicAuthenticationAccount";
|
||||||
NSString* const GCDWebServerOption_ConnectionClass = @"ConnectionClass";
|
NSString* const GCDWebServerOption_ConnectionClass = @"ConnectionClass";
|
||||||
NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET = @"AutomaticallyMapHEADToGET";
|
NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET = @"AutomaticallyMapHEADToGET";
|
||||||
NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval = @"ConnectedStateCoalescingInterval";
|
NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval = @"ConnectedStateCoalescingInterval";
|
||||||
@ -146,7 +150,8 @@ static void _SignalHandler(int signal) {
|
|||||||
|
|
||||||
@implementation GCDWebServer
|
@implementation GCDWebServer
|
||||||
|
|
||||||
@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, shouldAutomaticallyMapHEADToGET=_mapHEADToGET;
|
@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm,
|
||||||
|
authenticationBasicAccount=_authenticationBasicAccount, shouldAutomaticallyMapHEADToGET=_mapHEADToGET;
|
||||||
|
|
||||||
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
|
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
|
||||||
|
|
||||||
@ -339,6 +344,15 @@ static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValu
|
|||||||
return value ? value : defaultValue;
|
return value ? value : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline NSString* _EncodeBase64(NSString* string) {
|
||||||
|
NSData* data = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
#if (TARGET_OS_IPHONE && !(__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0)) || (!TARGET_OS_IPHONE && !(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9))
|
||||||
|
if (![data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) {
|
||||||
|
return [data base64Encoding];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return ARC_AUTORELEASE([[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding]);
|
||||||
|
}
|
||||||
- (BOOL)_start {
|
- (BOOL)_start {
|
||||||
DCHECK(_source == NULL);
|
DCHECK(_source == NULL);
|
||||||
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
||||||
@ -359,6 +373,9 @@ static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValu
|
|||||||
if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
|
if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
|
||||||
LOG_DEBUG(@"Did open listening socket %i", listeningSocket);
|
LOG_DEBUG(@"Did open listening socket %i", listeningSocket);
|
||||||
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
|
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
|
||||||
|
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
|
||||||
|
NSString* basicAuthenticationAccount = _GetOption(_options, GCDWebServerOption_BasicAuthenticationAccount, nil);
|
||||||
|
_authenticationBasicAccount = basicAuthenticationAccount.length ? _EncodeBase64(basicAuthenticationAccount) : nil;
|
||||||
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
|
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
|
||||||
_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];
|
||||||
@ -473,6 +490,10 @@ static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValu
|
|||||||
|
|
||||||
ARC_RELEASE(_serverName);
|
ARC_RELEASE(_serverName);
|
||||||
_serverName = nil;
|
_serverName = nil;
|
||||||
|
ARC_RELEASE(_authenticationRealm);
|
||||||
|
_authenticationRealm = nil;
|
||||||
|
ARC_RELEASE(_authenticationBasicAccount);
|
||||||
|
_authenticationBasicAccount = nil;
|
||||||
|
|
||||||
LOG_INFO(@"%@ stopped", [self class]);
|
LOG_INFO(@"%@ stopped", [self class]);
|
||||||
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
|
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
|
||||||
|
|||||||
@ -707,11 +707,24 @@ static NSString* _StringFromAddressData(NSData* data) {
|
|||||||
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {
|
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {
|
||||||
LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
||||||
GCDWebServerResponse* response = nil;
|
GCDWebServerResponse* response = nil;
|
||||||
@try {
|
BOOL authorized = YES;
|
||||||
response = block(request);
|
if (_server.authenticationBasicAccount) {
|
||||||
|
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
|
||||||
|
if (![authorizationHeader hasPrefix:@"Basic "] || ![[authorizationHeader substringFromIndex:6] isEqualToString:_server.authenticationBasicAccount]) {
|
||||||
|
authorized = NO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@catch (NSException* exception) {
|
if (authorized) {
|
||||||
LOG_EXCEPTION(exception);
|
@try {
|
||||||
|
response = block(request);
|
||||||
|
}
|
||||||
|
@catch (NSException* exception) {
|
||||||
|
LOG_EXCEPTION(exception);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
|
||||||
|
[response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"];
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,6 +129,8 @@ extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
|
|||||||
@interface GCDWebServer ()
|
@interface GCDWebServer ()
|
||||||
@property(nonatomic, readonly) NSArray* handlers;
|
@property(nonatomic, readonly) NSArray* handlers;
|
||||||
@property(nonatomic, readonly) NSString* serverName;
|
@property(nonatomic, readonly) NSString* serverName;
|
||||||
|
@property(nonatomic, readonly) NSString* authenticationRealm;
|
||||||
|
@property(nonatomic, readonly) NSString* authenticationBasicAccount;
|
||||||
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
|
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
|
||||||
- (void)willStartConnection:(GCDWebServerConnection*)connection;
|
- (void)willStartConnection:(GCDWebServerConnection*)connection;
|
||||||
- (void)didEndConnection:(GCDWebServerConnection*)connection;
|
- (void)didEndConnection:(GCDWebServerConnection*)connection;
|
||||||
|
|||||||
12
Mac/main.m
12
Mac/main.m
@ -130,9 +130,11 @@ int main(int argc, const char* argv[]) {
|
|||||||
BOOL recording = NO;
|
BOOL recording = NO;
|
||||||
NSString* rootDirectory = NSHomeDirectory();
|
NSString* rootDirectory = NSHomeDirectory();
|
||||||
NSString* testDirectory = nil;
|
NSString* testDirectory = nil;
|
||||||
|
NSString* authenticationRealm = nil;
|
||||||
|
NSString* authenticationAccount = nil;
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | webDAV | webUploader | streamingResponse] [-record] [-root directory] [-tests directory]\n\n", basename((char*)argv[0]));
|
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | webDAV | webUploader | streamingResponse] [-record] [-root directory] [-tests directory] [-authenticationRealm realm] [-authenticationAccount user:password]\n\n", basename((char*)argv[0]));
|
||||||
} else {
|
} else {
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
if (argv[i][0] != '-') {
|
if (argv[i][0] != '-') {
|
||||||
@ -161,6 +163,12 @@ int main(int argc, const char* argv[]) {
|
|||||||
} else if (!strcmp(argv[i], "-tests") && (i + 1 < argc)) {
|
} else if (!strcmp(argv[i], "-tests") && (i + 1 < argc)) {
|
||||||
++i;
|
++i;
|
||||||
testDirectory = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:argv[i] length:strlen(argv[i])] stringByStandardizingPath];
|
testDirectory = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:argv[i] length:strlen(argv[i])] stringByStandardizingPath];
|
||||||
|
} else if (!strcmp(argv[i], "-authenticationRealm") && (i + 1 < argc)) {
|
||||||
|
++i;
|
||||||
|
authenticationRealm = [NSString stringWithUTF8String:argv[i]];
|
||||||
|
} else if (!strcmp(argv[i], "-authenticationAccount") && (i + 1 < argc)) {
|
||||||
|
++i;
|
||||||
|
authenticationAccount = [NSString stringWithUTF8String:argv[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,6 +292,8 @@ int main(int argc, const char* argv[]) {
|
|||||||
NSMutableDictionary* options = [NSMutableDictionary dictionary];
|
NSMutableDictionary* options = [NSMutableDictionary dictionary];
|
||||||
[options setObject:@8080 forKey:GCDWebServerOption_Port];
|
[options setObject:@8080 forKey:GCDWebServerOption_Port];
|
||||||
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
||||||
|
[options setValue:authenticationRealm forKey:GCDWebServerOption_AuthenticationRealm];
|
||||||
|
[options setValue:authenticationAccount forKey:GCDWebServerOption_BasicAuthenticationAccount];
|
||||||
if ([webServer runWithOptions:options]) {
|
if ([webServer runWithOptions:options]) {
|
||||||
result = 0;
|
result = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,13 +18,14 @@ Extra built-in features:
|
|||||||
* [HTTP compression](https://en.wikipedia.org/wiki/HTTP_compression) with gzip for request and response HTTP bodies
|
* [HTTP compression](https://en.wikipedia.org/wiki/HTTP_compression) with gzip for request and response HTTP bodies
|
||||||
* [HTTP range](https://en.wikipedia.org/wiki/Byte_serving) support for requests of local files
|
* [HTTP range](https://en.wikipedia.org/wiki/Byte_serving) support for requests of local files
|
||||||
* Automatically handle transitions between foreground, background and suspended modes in iOS apps
|
* Automatically handle transitions between foreground, background and suspended modes in iOS apps
|
||||||
|
* [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) for simple password protection
|
||||||
|
|
||||||
Included extensions:
|
Included extensions:
|
||||||
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of ```GCDWebServer``` that implements an interface for uploading and downloading files using a web browser
|
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of ```GCDWebServer``` that implements an interface for uploading and downloading files using a web browser
|
||||||
* [GCDWebDAVServer](GCDWebDAVServer/GCDWebDAVServer.h): subclass of ```GCDWebServer``` that implements a class 1 [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server (with partial class 2 support for OS X Finder)
|
* [GCDWebDAVServer](GCDWebDAVServer/GCDWebDAVServer.h): subclass of ```GCDWebServer``` that implements a class 1 [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server (with partial class 2 support for OS X Finder)
|
||||||
|
|
||||||
What's not available out of the box but can be implemented on top of the API:
|
What's not available out of the box but can be implemented on top of the API:
|
||||||
* Authentication like [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
* Advanced authentication like [digest access](https://en.wikipedia.org/wiki/Digest_access_authentication)
|
||||||
* Web forms submitted using "multipart/mixed"
|
* Web forms submitted using "multipart/mixed"
|
||||||
|
|
||||||
What's not supported (but not really required from an embedded HTTP server):
|
What's not supported (but not really required from an embedded HTTP server):
|
||||||
|
|||||||
Reference in New Issue
Block a user