VLCHTTPConnection.m 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. //
  2. // VLCHTTPConnection.m
  3. // VLC for iOS
  4. //
  5. // Created by Felix Paul Kühne on 05.11.13.
  6. // Copyright (c) 2013 VideoLAN. All rights reserved.
  7. //
  8. // Refer to the COPYING file of the official project for license.
  9. //
  10. #import "VLCAppDelegate.h"
  11. #import "VLCHTTPConnection.h"
  12. #import "HTTPConnection.h"
  13. #import "MultipartFormDataParser.h"
  14. #import "HTTPMessage.h"
  15. #import "HTTPDataResponse.h"
  16. #import "HTTPFileResponse.h"
  17. #import "MultipartMessageHeaderField.h"
  18. @interface VLCHTTPConnection()
  19. {
  20. MultipartFormDataParser* _parser;
  21. NSFileHandle* _storeFile;
  22. NSMutableArray* _uploadedFiles;
  23. }
  24. @end
  25. @implementation VLCHTTPConnection
  26. - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
  27. {
  28. // Add support for POST
  29. if ([method isEqualToString:@"POST"]) {
  30. if ([path isEqualToString:@"/upload.json"])
  31. return YES;
  32. }
  33. return [super supportsMethod:method atPath:path];
  34. }
  35. - (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path
  36. {
  37. // Inform HTTP server that we expect a body to accompany a POST request
  38. if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
  39. // here we need to make sure, boundary is set in header
  40. NSString* contentType = [request headerField:@"Content-Type"];
  41. NSUInteger paramsSeparator = [contentType rangeOfString:@";"].location;
  42. if (NSNotFound == paramsSeparator)
  43. return NO;
  44. if (paramsSeparator >= contentType.length - 1)
  45. return NO;
  46. NSString* type = [contentType substringToIndex:paramsSeparator];
  47. if (![type isEqualToString:@"multipart/form-data"]) {
  48. // we expect multipart/form-data content type
  49. return NO;
  50. }
  51. // enumerate all params in content-type, and find boundary there
  52. NSArray* params = [[contentType substringFromIndex:paramsSeparator + 1] componentsSeparatedByString:@";"];
  53. for (NSString* param in params) {
  54. paramsSeparator = [param rangeOfString:@"="].location;
  55. if ((NSNotFound == paramsSeparator) || paramsSeparator >= param.length - 1)
  56. continue;
  57. NSString* paramName = [param substringWithRange:NSMakeRange(1, paramsSeparator-1)];
  58. NSString* paramValue = [param substringFromIndex:paramsSeparator+1];
  59. if ([paramName isEqualToString: @"boundary"])
  60. // let's separate the boundary from content-type, to make it more handy to handle
  61. [request setHeaderField:@"boundary" value:paramValue];
  62. }
  63. // check if boundary specified
  64. if (nil == [request headerField:@"boundary"])
  65. return NO;
  66. return YES;
  67. }
  68. return [super expectsRequestBodyFromMethod:method atPath:path];
  69. }
  70. - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
  71. {
  72. if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
  73. return [[HTTPDataResponse alloc] initWithData:[@"\"OK\"" dataUsingEncoding:NSUTF8StringEncoding]];
  74. }
  75. if ([method isEqualToString:@"GET"] && [path hasPrefix:@"/upload/"]) {
  76. // let download the uploaded files
  77. return [[HTTPFileResponse alloc] initWithFilePath: [[config documentRoot] stringByAppendingString:path] forConnection:self];
  78. }
  79. return [super httpResponseForMethod:method URI:path];
  80. }
  81. - (void)prepareForBodyWithSize:(UInt64)contentLength
  82. {
  83. // set up mime parser
  84. NSString* boundary = [request headerField:@"boundary"];
  85. _parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding];
  86. _parser.delegate = self;
  87. _uploadedFiles = [[NSMutableArray alloc] init];
  88. APLog(@"expecting file with size %lli", contentLength);
  89. }
  90. - (void)processBodyData:(NSData *)postDataChunk
  91. {
  92. /* append data to the parser. It will invoke callbacks to let us handle
  93. * parsed data. */
  94. [_parser appendData:postDataChunk];
  95. APLog(@"received %u bytes", postDataChunk.length);
  96. }
  97. - (void)processEpilogueData:(NSData*)data
  98. {
  99. APLog(@"%u bytes of epilogue", data.length);
  100. }
  101. //-----------------------------------------------------------------
  102. #pragma mark multipart form data parser delegate
  103. - (void)processStartOfPartWithHeader:(MultipartMessageHeader*) header
  104. {
  105. /* in this sample, we are not interested in parts, other then file parts.
  106. * check content disposition to find out filename */
  107. MultipartMessageHeaderField* disposition = (header.fields)[@"Content-Disposition"];
  108. NSString* filename = [(disposition.params)[@"filename"] lastPathComponent];
  109. if ((nil == filename) || [filename isEqualToString: @""]) {
  110. // it's either not a file part, or
  111. // an empty form sent. we won't handle it.
  112. return;
  113. }
  114. // create the path where to store the media
  115. NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  116. NSString* uploadDirPath = searchPaths[0];
  117. BOOL isDir = YES;
  118. if (![[NSFileManager defaultManager]fileExistsAtPath:uploadDirPath isDirectory:&isDir ]) {
  119. [[NSFileManager defaultManager]createDirectoryAtPath:uploadDirPath withIntermediateDirectories:YES attributes:nil error:nil];
  120. }
  121. NSString* filePath = [uploadDirPath stringByAppendingPathComponent: filename];
  122. if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
  123. _storeFile = nil;
  124. else {
  125. APLog(@"Saving file to %@", filePath);
  126. if (![[NSFileManager defaultManager] createDirectoryAtPath:uploadDirPath withIntermediateDirectories:true attributes:nil error:nil])
  127. APLog(@"Could not create directory at path: %@", filePath);
  128. if (![[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil])
  129. APLog(@"Could not create file at path: %@", filePath);
  130. _storeFile = [NSFileHandle fileHandleForWritingAtPath:filePath];
  131. [_uploadedFiles addObject: [NSString stringWithFormat:@"/upload/%@", filename]];
  132. [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
  133. [(VLCAppDelegate*)[UIApplication sharedApplication].delegate disableIdleTimer];
  134. }
  135. }
  136. - (void)processContent:(NSData*)data WithHeader:(MultipartMessageHeader*) header
  137. {
  138. // here we just write the output from parser to the file.
  139. if (_storeFile)
  140. [_storeFile writeData:data];
  141. }
  142. - (void)processEndOfPartWithHeader:(MultipartMessageHeader*)header
  143. {
  144. // as the file part is over, we close the file.
  145. APLog(@"closing file");
  146. [_storeFile closeFile];
  147. _storeFile = nil;
  148. [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
  149. [(VLCAppDelegate*)[UIApplication sharedApplication].delegate activateIdleTimer];
  150. /* update media library when file upload was completed */
  151. VLCAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
  152. [appDelegate updateMediaList];
  153. }
  154. - (void)finishBody
  155. {
  156. APLog(@"upload considered as complete");
  157. }
  158. @end