123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- //
- // MyHTTPConnection.m
- // WebServerIPhone
- //
- // CocoaLumberjack Demos
- //
- #import "MyHTTPConnection.h"
- #import "WebServerIPhoneAppDelegate.h"
- #import "HTTPLogging.h"
- #import "HTTPMessage.h"
- #import "HTTPDataResponse.h"
- #import "HTTPDynamicFileResponse.h"
- #import "GCDAsyncSocket.h"
- #import <CocoaLumberjack/CocoaLumberjack.h>
- #import "WebSocket.h"
- #import "WebSocketLogger.h"
- @implementation MyHTTPConnection
- static NSMutableSet *webSocketLoggers;
- /**
- * The runtime sends initialize to each class in a program exactly one time just before the class,
- * or any class that inherits from it, is sent its first message from within the program. (Thus the
- * method may never be invoked if the class is not used.) The runtime sends the initialize message to
- * classes in a thread-safe manner. Superclasses receive this message before their subclasses.
- *
- * This method may also be called directly (assumably by accident), hence the safety mechanism.
- **/
- + (void)initialize
- {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
-
- // We need some place to store the webSocketLogger instances.
- // So we'll store them here, in a class variable.
- //
- // We'll also use a simple notification system to release them when they die.
-
- webSocketLoggers = [[NSMutableSet alloc] init];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(webSocketLoggerDidDie:)
- name:WebSocketLoggerDidDieNotification
- object:nil];
- });
- }
- + (void)addWebSocketLogger:(WebSocketLogger *)webSocketLogger
- {
- @synchronized(webSocketLoggers)
- {
- [webSocketLoggers addObject:webSocketLogger];
- }
- }
- + (void)webSocketLoggerDidDie:(NSNotification *)notification
- {
- @synchronized(webSocketLoggers)
- {
- [webSocketLoggers removeObject:[notification object]];
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark Utilities
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- /**
- * Returns the logFileManager, which is a part of the DDFileLogger system.
- * The DDLogFileManager is the subsystem which manages the location and creation of log files.
- **/
- - (id <DDLogFileManager>)logFileManager
- {
- WebServerIPhoneAppDelegate *appDelegate;
- appDelegate = (WebServerIPhoneAppDelegate *)[[UIApplication sharedApplication] delegate];
-
- return appDelegate.fileLogger.logFileManager;
- }
- /**
- * Dynamic discovery of proper websocket href.
- **/
- - (NSString *)wsLocation
- {
- NSString *port = [NSString stringWithFormat:@"%hu", [asyncSocket localPort]];
-
- NSString *wsLocation;
- NSString *wsHost = [request headerField:@"Host"];
-
- if (wsHost == nil)
- {
- wsLocation = [NSString stringWithFormat:@"ws://localhost:%@/livelog", port];
- }
- else
- {
- wsLocation = [NSString stringWithFormat:@"ws://%@/livelog", wsHost];
- }
-
- return wsLocation;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark /logs.html
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- /**
- * Returns the response body for requests to "/logs/index.html".
- *
- * The response is generated dynamically.
- * It returns the list of log files currently on the system, along with their creation date and file size.
- **/
- - (NSData *)generateIndexData
- {
- NSArray *sortedLogFileInfos = [[self logFileManager] sortedLogFileInfos];
-
- NSDateFormatter *df = [[NSDateFormatter alloc] init];
- [df setFormatterBehavior:NSDateFormatterBehavior10_4];
- [df setDateFormat:@"yyyy/MM/dd HH:mm:ss"];
-
- NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
- [nf setFormatterBehavior:NSNumberFormatterBehavior10_4];
- [nf setNumberStyle:NSNumberFormatterDecimalStyle];
- [nf setMinimumFractionDigits:2];
- [nf setMaximumFractionDigits:2];
-
- NSMutableString *response = [NSMutableString stringWithCapacity:1000];
-
- [response appendString:@"<html><head>"];
- [response appendString:@"<style type='text/css'>@import url('styles.css');</style>"];
- [response appendString:@"</head><body>"];
-
- [response appendString:@"<h1>Device Log Files</h1>"];
-
- [response appendString:@"<table cellspacing='2'>"];
-
- for (DDLogFileInfo *logFileInfo in sortedLogFileInfos)
- {
- NSString *fileName = logFileInfo.fileName;
- NSString *fileDate = [df stringFromDate:[logFileInfo creationDate]];
- NSString *fileSize;
-
- unsigned long long sizeInBytes = logFileInfo.fileSize;
-
- double GBs = (double)(sizeInBytes) / (double)(1024 * 1024 * 1024);
- double MBs = (double)(sizeInBytes) / (double)(1024 * 1024);
- double KBs = (double)(sizeInBytes) / (double)(1024);
-
- if(GBs >= 1.0)
- {
- NSString *temp = [nf stringFromNumber:[NSNumber numberWithDouble:GBs]];
- fileSize = [NSString stringWithFormat:@"%@ GB", temp];
- }
- else if(MBs >= 1.0)
- {
- NSString *temp = [nf stringFromNumber:[NSNumber numberWithDouble:MBs]];
- fileSize = [NSString stringWithFormat:@"%@ MB", temp];
- }
- else
- {
- NSString *temp = [nf stringFromNumber:[NSNumber numberWithDouble:KBs]];
- fileSize = [NSString stringWithFormat:@"%@ KB", temp];
- }
-
- NSString *fileLink = [NSString stringWithFormat:@"<a href='/logs/%@'>%@</a>", fileName, fileName];
-
- [response appendFormat:@"<tr><td>%@</td><td>%@</td><td align='right'>%@</td>", fileLink, fileDate, fileSize];
- }
-
- [response appendString:@"</table></body></html>"];
-
- return [response dataUsingEncoding:NSUTF8StringEncoding];
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark HTTPConnection
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- /**
- * Overrides method in HTTPConnection.
- *
- * This method is invoked to retrieve the filePath for a given URI.
- * We override it to provide proper mapping for log file paths.
- **/
- - (NSString *)filePathForURI:(NSString *)path allowDirectory:(BOOL)allowDirectory
- {
- if ([path hasPrefix:@"/logs/"])
- {
- NSString *logsDir = [[self logFileManager] logsDirectory];
- return [logsDir stringByAppendingPathComponent:[path lastPathComponent]];
- }
-
- // Fall through
- return [super filePathForURI:path allowDirectory:allowDirectory];
- }
- /**
- * Overrides method in HTTPConnection.
- **/
- - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
- {
- if ([path isEqualToString:@"/logs.html"])
- {
- // Dynamically generate html response with list of available log files
-
- NSData *indexData = [self generateIndexData];
- return [[HTTPDataResponse alloc] initWithData:indexData];
- }
- else if ([path isEqualToString:@"/socket.html"])
- {
- // The socket.html file contains a URL template that needs to be completed:
- //
- // ws = new WebSocket("%%WEBSOCKET_URL%%");
- //
- // We need to replace "%%WEBSOCKET_URL%%" with whatever URL the server is running on.
- // We can accomplish this easily with the HTTPDynamicFileResponse class,
- // which takes a dictionary of replacement key-value pairs,
- // and performs replacements on the fly as it uploads the file.
-
- NSString *loc = [self wsLocation];
- NSDictionary *replacementDict = [NSDictionary dictionaryWithObject:loc forKey:@"WEBSOCKET_URL"];
-
- return [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
- forConnection:self
- separator:@"%%"
- replacementDictionary:replacementDict];
- }
-
- // Fall through
- return [super httpResponseForMethod:method URI:path];
- }
- /**
- * Overrides method in HTTPConnection.
- **/
- - (WebSocket *)webSocketForURI:(NSString *)path
- {
- if ([path isEqualToString:@"/livelog"])
- {
- // Create the WebSocket
- WebSocket *webSocket = [[WebSocket alloc] initWithRequest:request socket:asyncSocket];
-
- // Create the WebSocketLogger
- WebSocketLogger *webSocketLogger = [[WebSocketLogger alloc] initWithWebSocket:webSocket];
-
- [[self class] addWebSocketLogger:webSocketLogger];
- return webSocket;
- }
-
- return [super webSocketForURI:path];
- }
- @end
|