Permalink
Browse files

added http server and is serving basic content

  • Loading branch information...
schacon committed Dec 15, 2009
1 parent df4d8d9 commit 04b314581fd857aa042caad528708239d0f073a6
@@ -48,15 +48,19 @@ Copyright (C) 2008 Apple Inc. All Rights Reserved.
#import "BrowserViewController.h"
#import "ServerViewController.h"
+#import "MyHTTPConnection.h"
//CLASS INTERFACES:
+@class HTTPServer;
+
@interface AppController : NSObject <UIApplicationDelegate, UITabBarControllerDelegate>
{
UIWindow* _window;
UINavigationController *navigationController;
UITabBarController *tabBarController;
ServerViewController *serverViewController;
+ HTTPServer *httpServer;
NSString* gitDir;
}
@@ -12,6 +12,8 @@
#import "ProjectViewController.h"
#import "ProjectController.h"
#import "ServerViewController.h"
+#import "HTTPServer.h"
+#import "MyHTTPConnection.h"
#define bonIdentifier @"git"
@@ -79,6 +81,21 @@ - (void) applicationDidFinishLaunching:(UIApplication*)application
[atabBarController setViewControllers:vc animated:NO];
self.tabBarController = atabBarController;
+ // start the server
+ NSString *root = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) objectAtIndex:0];
+
+ httpServer = [HTTPServer new];
+ [httpServer setType:@"_http._tcp."];
+ [httpServer setConnectionClass:[MyHTTPConnection class]];
+ [httpServer setDocumentRoot:[NSURL fileURLWithPath:root]];
+ [httpServer setPort:8082];
+
+ NSError *error;
+ if(![httpServer start:&error])
+ {
+ NSLog(@"Error starting HTTP Server: %@", error);
+ }
+
// Configure and show the window
[_window addSubview:tabBarController.view];
[_window makeKeyAndVisible];
@@ -0,0 +1,22 @@
+//
+// This class was created by Nonnus,
+// who graciously decided to share it with the CocoaHTTPServer community.
+//
+
+#import <Foundation/Foundation.h>
+#import "HTTPConnection.h"
+
+
+@interface MyHTTPConnection : HTTPConnection
+{
+ int dataStartIndex;
+ NSMutableArray* multipartData;
+ BOOL postHeaderOK;
+}
+
+- (BOOL)isBrowseable:(NSString *)path;
+- (NSString *)createBrowseableIndex:(NSString *)path;
+
+- (BOOL)supportsPOST:(NSString *)path withSize:(UInt64)contentLength;
+
+@end
@@ -0,0 +1,260 @@
+//
+// This class was created by Nonnus,
+// who graciously decided to share it with the CocoaHTTPServer community.
+//
+
+#import "MyHTTPConnection.h"
+#import "HTTPServer.h"
+#import "HTTPResponse.h"
+#import "AsyncSocket.h"
+
+@implementation MyHTTPConnection
+
+/**
+ * Returns whether or not the requested resource is browseable.
+**/
+- (BOOL)isBrowseable:(NSString *)path
+{
+ // Override me to provide custom configuration...
+ // You can configure it for the entire server, or based on the current request
+
+ return YES;
+}
+
+
+/**
+ * This method creates a html browseable page.
+ * Customize to fit your needs
+**/
+- (NSString *)createBrowseableIndex:(NSString *)path
+{
+ NSArray *array = [[NSFileManager defaultManager] directoryContentsAtPath:path];
+
+ NSMutableString *outdata = [NSMutableString new];
+ [outdata appendString:@"<html><head>"];
+ [outdata appendFormat:@"<title>Files from %@</title>", server.name];
+ [outdata appendString:@"<style>html {background-color:#eeeeee} body { background-color:#FFFFFF; font-family:Tahoma,Arial,Helvetica,sans-serif; font-size:18x; margin-left:15%; margin-right:15%; border:3px groove #006600; padding:15px; } </style>"];
+ [outdata appendString:@"</head><body>"];
+ [outdata appendFormat:@"<h1>Files from %@</h1>", server.name];
+ [outdata appendString:@"<bq>The following files are hosted live from the iPhone's Docs folder.</bq>"];
+ [outdata appendString:@"<p>"];
+ [outdata appendFormat:@"<a href=\"..\">..</a><br />\n"];
+ for (NSString *fname in array)
+ {
+ NSDictionary *fileDict = [[NSFileManager defaultManager] fileAttributesAtPath:[path stringByAppendingPathComponent:fname] traverseLink:NO];
+ //NSLog(@"fileDict: %@", fileDict);
+ NSString *modDate = [[fileDict objectForKey:NSFileModificationDate] description];
+ if ([[fileDict objectForKey:NSFileType] isEqualToString: @"NSFileTypeDirectory"]) fname = [fname stringByAppendingString:@"/"];
+ [outdata appendFormat:@"<a href=\"%@\">%@</a> (%8.1f Kb, %@)<br />\n", fname, fname, [[fileDict objectForKey:NSFileSize] floatValue] / 1024, modDate];
+ }
+ [outdata appendString:@"</p>"];
+
+ if ([self supportsPOST:path withSize:0])
+ {
+ [outdata appendString:@"<form action=\"\" method=\"post\" enctype=\"multipart/form-data\" name=\"form1\" id=\"form1\">"];
+ [outdata appendString:@"<label>upload file"];
+ [outdata appendString:@"<input type=\"file\" name=\"file\" id=\"file\" />"];
+ [outdata appendString:@"</label>"];
+ [outdata appendString:@"<label>"];
+ [outdata appendString:@"<input type=\"submit\" name=\"button\" id=\"button\" value=\"Submit\" />"];
+ [outdata appendString:@"</label>"];
+ [outdata appendString:@"</form>"];
+ }
+
+ [outdata appendString:@"</body></html>"];
+
+ //NSLog(@"outData: %@", outdata);
+ return [outdata autorelease];
+}
+
+
+- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)relativePath
+{
+ if ([@"POST" isEqualToString:method])
+ {
+ return YES;
+ }
+
+ return [super supportsMethod:method atPath:relativePath];
+}
+
+
+/**
+ * Returns whether or not the server will accept POSTs.
+ * That is, whether the server will accept uploaded data for the given URI.
+**/
+- (BOOL)supportsPOST:(NSString *)path withSize:(UInt64)contentLength
+{
+// NSLog(@"POST:%@", path);
+
+ dataStartIndex = 0;
+ multipartData = [[NSMutableArray alloc] init];
+ postHeaderOK = FALSE;
+
+ return YES;
+}
+
+
+/**
+ * This method is called to get a response for a request.
+ * You may return any object that adopts the HTTPResponse protocol.
+ * The HTTPServer comes with two such classes: HTTPFileResponse and HTTPDataResponse.
+ * HTTPFileResponse is a wrapper for an NSFileHandle object, and is the preferred way to send a file response.
+ * HTTPDataResopnse is a wrapper for an NSData object, and may be used to send a custom response.
+**/
+- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
+{
+ NSLog(@"httpResponseForURI: method:%@ path:%@", method, path);
+
+ NSData *requestData = [(NSData *)CFHTTPMessageCopySerializedMessage(request) autorelease];
+
+ NSString *requestStr = [[[NSString alloc] initWithData:requestData encoding:NSASCIIStringEncoding] autorelease];
+ NSLog(@"\n=== Request ====================\n%@\n================================", requestStr);
+
+ if (requestContentLength > 0) // Process POST data
+ {
+ NSLog(@"processing post data: %i", requestContentLength);
+
+ if ([multipartData count] < 2) return nil;
+
+ NSString* postInfo = [[NSString alloc] initWithBytes:[[multipartData objectAtIndex:1] bytes]
+ length:[[multipartData objectAtIndex:1] length]
+ encoding:NSUTF8StringEncoding];
+
+ NSArray* postInfoComponents = [postInfo componentsSeparatedByString:@"; filename="];
+ postInfoComponents = [[postInfoComponents lastObject] componentsSeparatedByString:@"\""];
+ postInfoComponents = [[postInfoComponents objectAtIndex:1] componentsSeparatedByString:@"\\"];
+ NSString* filename = [postInfoComponents lastObject];
+
+ if (![filename isEqualToString:@""]) //this makes sure we did not submitted upload form without selecting file
+ {
+ UInt16 separatorBytes = 0x0A0D;
+ NSMutableData* separatorData = [NSMutableData dataWithBytes:&separatorBytes length:2];
+ [separatorData appendData:[multipartData objectAtIndex:0]];
+ int l = [separatorData length];
+ int count = 2; //number of times the separator shows up at the end of file data
+
+ NSFileHandle* dataToTrim = [multipartData lastObject];
+ NSLog(@"data: %@", dataToTrim);
+
+ unsigned long long i;
+ for (i = [dataToTrim offsetInFile] - l; i > 0; i--)
+ {
+ [dataToTrim seekToFileOffset:i];
+ if ([[dataToTrim readDataOfLength:l] isEqualToData:separatorData])
+ {
+ [dataToTrim truncateFileAtOffset:i];
+ i -= l;
+ if (--count == 0) break;
+ }
+ }
+
+ NSLog(@"NewFileUploaded");
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"NewFileUploaded" object:nil];
+ }
+
+ int n;
+ for (n = 1; n < [multipartData count] - 1; n++)
+ NSLog(@"%@", [[NSString alloc] initWithBytes:[[multipartData objectAtIndex:n] bytes] length:[[multipartData objectAtIndex:n] length] encoding:NSUTF8StringEncoding]);
+
+ [postInfo release];
+ [multipartData release];
+ requestContentLength = 0;
+
+ }
+
+ NSString *filePath = [self filePathForURI:path];
+
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
+ {
+ return [[[HTTPFileResponse alloc] initWithFilePath:filePath] autorelease];
+ }
+ else
+ {
+ NSString *folder = [path isEqualToString:@"/"] ? [[server documentRoot] path] : [NSString stringWithFormat: @"%@%@", [[server documentRoot] path], path];
+
+ if ([self isBrowseable:folder])
+ {
+ //NSLog(@"folder: %@", folder);
+ NSData *browseData = [[self createBrowseableIndex:folder] dataUsingEncoding:NSUTF8StringEncoding];
+ return [[[HTTPDataResponse alloc] initWithData:browseData] autorelease];
+ }
+ }
+
+ return nil;
+}
+
+
+/**
+ * This method is called to handle data read from a POST.
+ * The given data is part of the POST body.
+**/
+- (void)processDataChunk:(NSData *)postDataChunk
+{
+ // Override me to do something useful with a POST.
+ // If the post is small, such as a simple form, you may want to simply append the data to the request.
+ // If the post is big, such as a file upload, you may want to store the file to disk.
+ //
+ // Remember: In order to support LARGE POST uploads, the data is read in chunks.
+ // This prevents a 50 MB upload from being stored in RAM.
+ // The size of the chunks are limited by the POST_CHUNKSIZE definition.
+ // Therefore, this method may be called multiple times for the same POST request.
+
+ //NSLog(@"processPostDataChunk");
+
+ if (!postHeaderOK)
+ {
+ UInt16 separatorBytes = 0x0A0D;
+ NSData* separatorData = [NSData dataWithBytes:&separatorBytes length:2];
+
+ int l = [separatorData length];
+ int i;
+ for (i = 0; i < [postDataChunk length] - l; i++)
+ {
+ NSRange searchRange = {i, l};
+
+ if ([[postDataChunk subdataWithRange:searchRange] isEqualToData:separatorData])
+ {
+ NSRange newDataRange = {dataStartIndex, i - dataStartIndex};
+ dataStartIndex = i + l;
+ i += l - 1;
+ NSData *newData = [postDataChunk subdataWithRange:newDataRange];
+
+ if ([newData length])
+ {
+ [multipartData addObject:newData];
+ }
+ else
+ {
+ postHeaderOK = TRUE;
+
+ NSString* postInfo = [[NSString alloc] initWithBytes:[[multipartData objectAtIndex:1] bytes] length:[[multipartData objectAtIndex:1] length] encoding:NSUTF8StringEncoding];
+ NSArray* postInfoComponents = [postInfo componentsSeparatedByString:@"; filename="];
+ postInfoComponents = [[postInfoComponents lastObject] componentsSeparatedByString:@"\""];
+ postInfoComponents = [[postInfoComponents objectAtIndex:1] componentsSeparatedByString:@"\\"];
+ NSString* filename = [[[server documentRoot] path] stringByAppendingPathComponent:[postInfoComponents lastObject]];
+ NSRange fileDataRange = {dataStartIndex, [postDataChunk length] - dataStartIndex};
+
+ [[NSFileManager defaultManager] createFileAtPath:filename contents:[postDataChunk subdataWithRange:fileDataRange] attributes:nil];
+ NSFileHandle *file = [[NSFileHandle fileHandleForUpdatingAtPath:filename] retain];
+
+ if (file)
+ {
+ [file seekToEndOfFile];
+ [multipartData addObject:file];
+ }
+
+ [postInfo release];
+
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ [(NSFileHandle*)[multipartData lastObject] writeData:postDataChunk];
+ }
+}
+
+@end
Oops, something went wrong.

0 comments on commit 04b3145

Please sign in to comment.