Skip to content
This repository has been archived by the owner on Oct 9, 2020. It is now read-only.

Commit

Permalink
Adding directoryIndexFileNames method, to allow easy support of multi…
Browse files Browse the repository at this point in the history
…ple index file names ("index.html", "index.htm", etc). Also altering a bit of code to workaround a bug in NSURL.
  • Loading branch information
robbiehanson committed Sep 12, 2010
1 parent 56d49a1 commit 1a7c4e9
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 31 deletions.
1 change: 1 addition & 0 deletions HTTPConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@

- (NSString *)requestURI;

- (NSArray *)directoryIndexFileNames;
- (NSString *)filePathForURI:(NSString *)path;
- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path;
- (WebSocket *)webSocketForURI:(NSString *)path;
Expand Down
112 changes: 81 additions & 31 deletions HTTPConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,17 @@ - (void)addBasicAuthChallenge:(CFHTTPMessageRef)response
#pragma mark Core
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Starts reading an HTTP request.
**/
- (void)startReadingRequest
{
[asyncSocket readDataToData:[AsyncSocket CRLFData]
withTimeout:READ_TIMEOUT
maxLength:LIMIT_MAX_HEADER_LINE_LENGTH
tag:HTTP_REQUEST_HEADER];
}

/**
* Parses the given query string.
*
Expand Down Expand Up @@ -1341,6 +1352,17 @@ - (void)continueSendingMultiRangeResponseBody
#pragma mark Responses
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Returns an array of possible index pages.
* For example: {"index.html", "index.htm"}
**/
- (NSArray *)directoryIndexFileNames
{
// Override me to support other index pages.

return [NSArray arrayWithObjects:@"index.html", @"index.htm", nil];
}

/**
* Converts relative URI path into full file-system path.
**/
Expand All @@ -1349,41 +1371,73 @@ - (NSString *)filePathForURI:(NSString *)path
// Override me to perform custom path mapping.
// For example you may want to use a default file other than index.html, or perhaps support multiple types.

// If there is no configured documentRoot, then it makes no sense to try to return anything
if(![server documentRoot]) return nil;
NSURL *documentRoot = [server documentRoot];

// If there is no configured documentRoot,
// then it makes no sense to try to return anything.
if (![server documentRoot])
{
return nil;
}

// Part 1: Strip parameters from the url
//
// E.g.: /page.html?q=22&var=abc -> /page.html

NSString *relativePath = [[NSURL URLWithString:path relativeToURL:documentRoot] relativePath];

// Convert path to a relative path.
// This essentially means trimming beginning '/' characters.
// Beware of a bug in the Cocoa framework:
// Part 2: Append relative path to document root (base path)
//
// E.g.: relativePath="/images/icon.png"
// documentRoot="/Users/robbie/Sites"
// fullPath="/Users/robbie/Sites/images/icon.png"
//
// We also standardize the path.
//
// [NSURL URLWithString:@"/foo" relativeToURL:baseURL] == @"/baseURL/foo"
// [NSURL URLWithString:@"/foo%20bar" relativeToURL:baseURL] == @"/foo bar"
// [NSURL URLWithString:@"/foo" relativeToURL:baseURL] == @"/foo"
// E.g.: "Users/robbie/Sites/images/../index.html" -> "/Users/robbie/Sites/index.html"

NSString *relativePath = path;
NSString *basePath = [documentRoot path];

while([relativePath hasPrefix:@"/"] && [relativePath length] > 1)
NSString *fullPath = [[basePath stringByAppendingPathComponent:relativePath] stringByStandardizingPath];

// Part 3: Prevent serving files outside the document root.
//
// Sneaky requests may include ".." in the path.
//
// E.g.: relativePath="../Documents/TopSecret.doc"
// documentRoot="/Users/robbie/Sites"
// fullPath="/Users/robbie/Documents/TopSecret.doc"

if (![fullPath hasPrefix:basePath])
{
relativePath = [relativePath substringFromIndex:1];
return nil;
}

NSURL *url;
// Part 4: Search for index page if path is pointing to a directory

BOOL isDir = NO;

if([relativePath hasSuffix:@"/"])
if ([[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDir] && isDir)
{
NSString *completedRelativePath = [relativePath stringByAppendingString:@"index.html"];
url = [NSURL URLWithString:completedRelativePath relativeToURL:[server documentRoot]];
NSArray *indexFileNames = [self directoryIndexFileNames];

for (NSString *indexFileName in indexFileNames)
{
NSString *indexFilePath = [fullPath stringByAppendingPathComponent:indexFileName];

if ([[NSFileManager defaultManager] fileExistsAtPath:indexFilePath isDirectory:&isDir] && !isDir)
{
return indexFilePath;
}
}

// No matching index files found in directory
return nil;
}
else
{
url = [NSURL URLWithString:relativePath relativeToURL:[server documentRoot]];
return fullPath;
}

// Watch out for sneaky requests with ".." in the path
// For example, the following request: "../Documents/TopSecret.doc"
if(![[url path] hasPrefix:[[server documentRoot] path]]) return nil;

return [[url path] stringByStandardizingPath];
}

/**
Expand All @@ -1399,7 +1453,9 @@ - (NSString *)filePathForURI:(NSString *)path

NSString *filePath = [self filePathForURI:path];

if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
BOOL isDir = NO;

if (filePath && [[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDir] && !isDir)
{
return [[[HTTPFileResponse alloc] initWithFilePath:filePath] autorelease];

Expand Down Expand Up @@ -1747,10 +1803,7 @@ - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UIn
{
// The socket is up and ready, and this method is called on the socket's corresponding thread.
// We can now start reading the HTTP requests...
[asyncSocket readDataToData:[AsyncSocket CRLFData]
withTimeout:READ_TIMEOUT
maxLength:LIMIT_MAX_HEADER_LINE_LENGTH
tag:HTTP_REQUEST_HEADER];
[self startReadingRequest];
}

/**
Expand Down Expand Up @@ -2006,10 +2059,7 @@ - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
numHeaderLines = 0;

// And start listening for more requests
[asyncSocket readDataToData:[AsyncSocket CRLFData]
withTimeout:READ_TIMEOUT
maxLength:LIMIT_MAX_HEADER_LINE_LENGTH
tag:HTTP_REQUEST_HEADER];
[self startReadingRequest];
}
}
}
Expand Down

0 comments on commit 1a7c4e9

Please sign in to comment.