HTTPConnection.m.svn-base
上传用户:kc0325
上传日期:2020-06-20
资源大小:204k
文件大小:54k
源码类别:

iPhone

开发平台:

Objective-C

  1. #import "AsyncSocket.h"
  2. #import "HTTPServer.h"
  3. #import "HTTPConnection.h"
  4. #import "HTTPResponse.h"
  5. #import "HTTPAuthenticationRequest.h"
  6. #import "DDNumber.h"
  7. #import "DDRange.h"
  8. #import "DDData.h"
  9. // Define chunk size used to read in data for responses
  10. // This is how much data will be read from disk into RAM at a time
  11. #if TARGET_OS_IPHONE
  12.   #define READ_CHUNKSIZE  (1024 * 128)
  13. #else
  14.   #define READ_CHUNKSIZE  (1024 * 512)
  15. #endif
  16. // Define chunk size used to read in POST upload data
  17. #if TARGET_OS_IPHONE
  18.   #define POST_CHUNKSIZE  (1024 * 32)
  19. #else
  20.   #define POST_CHUNKSIZE  (1024 * 128)
  21. #endif
  22. // Define the various timeouts (in seconds) for various parts of the HTTP process
  23. #define READ_TIMEOUT          -1
  24. #define WRITE_HEAD_TIMEOUT    30
  25. #define WRITE_BODY_TIMEOUT    -1
  26. #define WRITE_ERROR_TIMEOUT   30
  27. #define NONCE_TIMEOUT        300
  28. // Define the various limits
  29. // LIMIT_MAX_HEADER_LINE_LENGTH: Max length (in bytes) of any single line in a header (including rn)
  30. // LIMIT_MAX_HEADER_LINES      : Max number of lines in a single header (including first GET line)
  31. #define LIMIT_MAX_HEADER_LINE_LENGTH  8190
  32. #define LIMIT_MAX_HEADER_LINES         100
  33. // Define the various tags we'll use to differentiate what it is we're currently doing
  34. #define HTTP_REQUEST_HEADER                15
  35. #define HTTP_REQUEST_BODY                  16
  36. #define HTTP_PARTIAL_RESPONSE              24
  37. #define HTTP_PARTIAL_RESPONSE_HEADER       25
  38. #define HTTP_PARTIAL_RESPONSE_BODY         26
  39. #define HTTP_PARTIAL_RANGE_RESPONSE_BODY   28
  40. #define HTTP_PARTIAL_RANGES_RESPONSE_BODY  29
  41. #define HTTP_RESPONSE                      30
  42. #define HTTP_FINAL_RESPONSE                45
  43. // A quick note about the tags:
  44. // 
  45. // The HTTP_RESPONSE and HTTP_FINAL_RESPONSE are designated tags signalling that the response is completely sent.
  46. // That is, in the onSocket:didWriteDataWithTag: method, if the tag is HTTP_RESPONSE or HTTP_FINAL_RESPONSE,
  47. // it is assumed that the response is now completely sent.
  48. // Use HTTP_RESPONSE if it's the end a response, and you want to start reading more requests afterwards.
  49. // Use HTTP_FINAL_RESPONSE if you wish to terminate the connection after sending the response.
  50. // 
  51. // If you are sending multiple data segments in a custom response, make sure that only the last segment has
  52. // the HTTP_RESPONSE tag. For all other segments prior to the last segment use HTTP_PARTIAL_RESPONSE, or some other
  53. // tag of your own invention.
  54. @interface HTTPConnection (PrivateAPI)
  55. - (CFHTTPMessageRef)prepareUniRangeResponse:(UInt64)contentLength;
  56. - (CFHTTPMessageRef)prepareMultiRangeResponse:(UInt64)contentLength;
  57. @end
  58. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  59. #pragma mark -
  60. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  61. @implementation HTTPConnection
  62. static NSMutableArray *recentNonces;
  63. /**
  64.  * This method is automatically called (courtesy of Cocoa) before the first instantiation of this class.
  65.  * We use it to initialize any static variables.
  66. **/
  67. + (void)initialize
  68. {
  69. static BOOL initialized = NO;
  70. if(!initialized)
  71. {
  72. // Initialize class variables
  73. recentNonces = [[NSMutableArray alloc] initWithCapacity:5];
  74. initialized = YES;
  75. }
  76. }
  77. /**
  78.  * This method is designed to be called by a scheduled timer, and will remove a nonce from the recent nonce list.
  79.  * The nonce to remove should be set as the timer's userInfo.
  80. **/
  81. + (void)removeRecentNonce:(NSTimer *)aTimer
  82. {
  83. [recentNonces removeObject:[aTimer userInfo]];
  84. }
  85. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  86. #pragma mark Init, Dealloc:
  87. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  88. /**
  89.  * Sole Constructor.
  90.  * Associates this new HTTP connection with the given AsyncSocket.
  91.  * This HTTP connection object will become the socket's delegate and take over responsibility for the socket.
  92. **/
  93. - (id)initWithAsyncSocket:(AsyncSocket *)newSocket forServer:(HTTPServer *)myServer
  94. {
  95. if(self = [super init])
  96. {
  97. // Take over ownership of the socket
  98. asyncSocket = [newSocket retain];
  99. [asyncSocket setDelegate:self];
  100. // Enable pre-buffering on the socket to improve readDataToData performance
  101. [asyncSocket enablePreBuffering];
  102. // Store reference to server
  103. // Note that we do not retain the server. Parents retain their children, children do not retain their parents.
  104. server = myServer;
  105. // Initialize lastNC (last nonce count)
  106. // These must increment for each request from the client
  107. lastNC = 0;
  108. // Create a new HTTP message
  109. // Note the second parameter is YES, because it will be used for HTTP requests from the client
  110. request = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, YES);
  111. numHeaderLines = 0;
  112. // And now that we own the socket, and we have our CFHTTPMessage object (for requests) ready,
  113. // we can start reading the HTTP requests...
  114. [asyncSocket readDataToData:[AsyncSocket CRLFData]
  115. withTimeout:READ_TIMEOUT
  116.   maxLength:LIMIT_MAX_HEADER_LINE_LENGTH
  117. tag:HTTP_REQUEST_HEADER];
  118. }
  119. return self;
  120. }
  121. /**
  122.  * Standard Deconstructor.
  123. **/
  124. - (void)dealloc
  125. {
  126. [asyncSocket setDelegate:nil];
  127. [asyncSocket disconnect];
  128. [asyncSocket release];
  129. if(request) CFRelease(request);
  130. [nonce release];
  131. [httpResponse release];
  132. [ranges release];
  133. [ranges_headers release];
  134. [ranges_boundry release];
  135. [super dealloc];
  136. }
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  138. #pragma mark Connection Control:
  139. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  140. /**
  141.  * Returns whether or not the server will accept POSTs.
  142.  * That is, whether the server will accept uploaded data for the given URI.
  143. **/
  144. - (BOOL)supportsPOST:(NSString *)path withSize:(UInt64)contentLength
  145. {
  146. //NSLog(@"file: %@", path);
  147. dataStartIndex = 0;
  148. multipartData = [[NSMutableArray new] retain];
  149. postHeaderOK = FALSE;
  150. // Override me to support POST uploads.
  151. // Things to consider:
  152. // - Does the given path represent a resource that is designed to accept POST data?
  153. // - Is the size of the data being uploaded too big?
  154. // 
  155. // For more information, you can always access the CFHTTPMessageRef request variable.
  156. return YES;
  157. }
  158. /**
  159.  * Returns whether or not the server is configured to be a secure server.
  160.  * In other words, all connections to this server are immediately secured, thus only secure connections are allowed.
  161.  * This is the equivalent of having an https server, where it is assumed that all connections must be secure.
  162.  * If this is the case, then unsecure connections will not be allowed on this server, and a separate unsecure server
  163.  * would need to be run on a separate port in order to support unsecure connections.
  164.  * 
  165.  * Note: In order to support secure connections, the sslIdentityAndCertificates method must be implemented.
  166. **/
  167. - (BOOL)isSecureServer
  168. {
  169. // Override me to create an https server...
  170. return NO;
  171. }
  172. /**
  173.  * This method is expected to returns an array appropriate for use in kCFStreamSSLCertificates SSL Settings.
  174.  * It should be an array of SecCertificateRefs except for the first element in the array, which is a SecIdentityRef.
  175. **/
  176. - (NSArray *)sslIdentityAndCertificates
  177. {
  178. // Override me to provide the proper required SSL identity.
  179. // You can configure the identity for the entire server, or based on the current request
  180. return nil;
  181. }
  182. /**
  183.  * Returns whether or not the requested resource is brwoseable.
  184.  **/
  185. - (BOOL)isBrowseable:(NSString *)path
  186. {
  187. // Override me to provide custom configuration...
  188. // You can configure it for the entire server, or based on the current request
  189. return YES;
  190. }
  191. /**
  192.  * Returns whether or not the requested resource is password protected.
  193.  * In this generic implementation, nothing is password protected.
  194. **/
  195. - (BOOL)isPasswordProtected:(NSString *)path
  196. {
  197. // Override me to provide password protection...
  198. // You can configure it for the entire server, or based on the current request
  199. return NO;
  200. }
  201. /**
  202.  * Returns whether or not the authentication challenge should use digest access authentication.
  203.  * The alternative is basic authentication.
  204.  * 
  205.  * If at all possible, digest access authentication should be used because it's more secure.
  206.  * Basic authentication sends passwords in the clear and should be avoided unless using SSL/TLS.
  207. **/
  208. - (BOOL)useDigestAccessAuthentication
  209. {
  210. // Override me to use customize the authentication scheme
  211. // Make sure you understand the security consequences of using basic authentication
  212. return YES;
  213. }
  214. /**
  215.  * Returns the authentication realm.
  216.  * In this generic implmentation, a default realm is used for the entire server.
  217. **/
  218. - (NSString *)realm
  219. {
  220. // Override me to provide a custom realm...
  221. // You can configure it for the entire server, or based on the current request
  222. return @"defaultRealm@host.com";
  223. }
  224. /**
  225.  * Returns the password for the given username.
  226.  * This password will be used to generate the response hash to validate against the given response hash.
  227. **/
  228. - (NSString *)passwordForUser:(NSString *)username
  229. {
  230. // Override me to provide proper password authentication
  231. // You can configure a password for the entire server, or custom passwords for users and/or resources
  232. // Note: A password of nil, or a zero-length password is considered the equivalent of no password
  233. return nil;
  234. }
  235. /**
  236.  * Generates and returns an authentication nonce.
  237.  * A nonce is a  server-specified string uniquely generated for each 401 response.
  238.  * The default implementation uses a single nonce for each session.
  239. **/
  240. - (NSString *)generateNonce
  241. {
  242. // We use the Core Foundation UUID class to generate a nonce value for us
  243. // UUIDs (Universally Unique Identifiers) are 128-bit values guaranteed to be unique.
  244. CFUUIDRef theUUID = CFUUIDCreate(NULL);
  245.     NSString *newNonce = [(NSString *)CFUUIDCreateString(NULL, theUUID) autorelease];
  246.     CFRelease(theUUID);
  247. // We have to remember that the HTTP protocol is stateless
  248. // Even though with version 1.1 persistent connections are the norm, they are not guaranteed
  249. // Thus if we generate a nonce for this connection,
  250. // it should be honored for other connections in the near future
  251. // 
  252. // In fact, this is absolutely necessary in order to support QuickTime
  253. // When QuickTime makes it's initial connection, it will be unauthorized, and will receive a nonce
  254. // It then disconnects, and creates a new connection with the nonce, and proper authentication
  255. // If we don't honor the nonce for the second connection, QuickTime will repeat the process and never connect
  256. [recentNonces addObject:newNonce];
  257. [NSTimer scheduledTimerWithTimeInterval:NONCE_TIMEOUT
  258.  target:[HTTPConnection class]
  259.    selector:@selector(removeRecentNonce:)
  260.    userInfo:newNonce
  261. repeats:NO];
  262. return newNonce;
  263. }
  264. /**
  265.  * Returns whether or not the user is properly authenticated.
  266.  * Authentication is done using Digest Access Authentication accoring to RFC 2617.
  267. **/
  268. - (BOOL)isAuthenticated
  269. {
  270. // Extract the authentication information from the Authorization header
  271. HTTPAuthenticationRequest *auth = [[[HTTPAuthenticationRequest alloc] initWithRequest:request] autorelease];
  272. if([self useDigestAccessAuthentication])
  273. {
  274. // Digest Access Authentication
  275. if(![auth isDigest])
  276. {
  277. // User didn't send proper digest access authentication credentials
  278. return NO;
  279. }
  280. if([auth username] == nil)
  281. {
  282. // The client didn't provide a username
  283. // Most likely they didn't provide any authentication at all
  284. return NO;
  285. }
  286. NSString *password = [self passwordForUser:[auth username]];
  287. if((password == nil) || ([password length] == 0))
  288. {
  289. // There is no password set, or the password is an empty string
  290. // We can consider this the equivalent of not using password protection
  291. return YES;
  292. }
  293. NSString *method = [(NSString *)CFHTTPMessageCopyRequestMethod(request) autorelease];
  294. NSURL *absoluteUrl = [(NSURL *)CFHTTPMessageCopyRequestURL(request) autorelease];
  295. NSString *url = [(NSURL *)absoluteUrl relativeString];
  296. if(![url isEqualToString:[auth uri]])
  297. {
  298. // Requested URL and Authorization URI do not match
  299. // This could be a replay attack
  300. // IE - attacker provides same authentication information, but requests a different resource
  301. return NO;
  302. }
  303. // The nonce the client provided will most commonly be stored in our local (cached) nonce variable
  304. if(![nonce isEqualToString:[auth nonce]])
  305. {
  306. // The given nonce may be from another connection
  307. // We need to search our list of recent nonce strings that have been recently distributed
  308. if([recentNonces containsObject:[auth nonce]])
  309. {
  310. // Store nonce in local (cached) nonce variable to prevent array searches in the future
  311. [nonce release];
  312. nonce = [[auth nonce] copy];
  313. // The client has switched to using a different nonce value
  314. // This may happen if the client tries to get a file in a directory with different credentials.
  315. // The previous credentials wouldn't work, and the client would receive a 401 error
  316. // along with a new nonce value. The client then uses this new nonce value and requests the file again.
  317. // Whatever the case may be, we need to reset lastNC, since that variable is on a per nonce basis.
  318. lastNC = 0;
  319. }
  320. else
  321. {
  322. // We have no knowledge of ever distributing such a nonce
  323. // This could be a replay attack from a previous connection in the past
  324. return NO;
  325. }
  326. }
  327. if([[auth nc] intValue] <= lastNC)
  328. {
  329. // The nc value (nonce count) hasn't been incremented since the last request
  330. // This could be a replay attack
  331. return NO;
  332. }
  333. lastNC = [[auth nc] intValue];
  334. NSString *HA1str = [NSString stringWithFormat:@"%@:%@:%@", [auth username], [auth realm], password];
  335. NSString *HA2str = [NSString stringWithFormat:@"%@:%@", method, [auth uri]];
  336. NSString *HA1 = [[[HA1str dataUsingEncoding:NSUTF8StringEncoding] md5Digest] hexStringValue];
  337. NSString *HA2 = [[[HA2str dataUsingEncoding:NSUTF8StringEncoding] md5Digest] hexStringValue];
  338. NSString *responseStr = [NSString stringWithFormat:@"%@:%@:%@:%@:%@:%@",
  339.  HA1, [auth nonce], [auth nc], [auth cnonce], [auth qop], HA2];
  340. NSString *response = [[[responseStr dataUsingEncoding:NSUTF8StringEncoding] md5Digest] hexStringValue];
  341. return [response isEqualToString:[auth response]];
  342. }
  343. else
  344. {
  345. // Basic Authentication
  346. if(![auth isBasic])
  347. {
  348. // User didn't send proper base authentication credentials
  349. return NO;
  350. }
  351. // Decode the base 64 encoded credentials
  352. NSString *base64Credentials = [auth base64Credentials];
  353. NSData *temp = [[base64Credentials dataUsingEncoding:NSUTF8StringEncoding] base64Decoded];
  354. NSString *credentials = [[[NSString alloc] initWithData:temp encoding:NSUTF8StringEncoding] autorelease];
  355. // The credentials should be of the form "username:password"
  356. // The username is not allowed to contain a colon
  357. NSRange colonRange = [credentials rangeOfString:@":"];
  358. if(colonRange.length == 0)
  359. {
  360. // Malformed credentials
  361. return NO;
  362. }
  363. NSString *credUsername = [credentials substringToIndex:colonRange.location];
  364. NSString *credPassword = [credentials substringFromIndex:(colonRange.location + colonRange.length)];
  365. NSString *password = [self passwordForUser:credUsername];
  366. if((password == nil) || ([password length] == 0))
  367. {
  368. // There is no password set, or the password is an empty string
  369. // We can consider this the equivalent of not using password protection
  370. return YES;
  371. }
  372. return [password isEqualToString:credPassword];
  373. }
  374. }
  375. /**
  376.  * Adds a digest access authentication challenge to the given response.
  377. **/
  378. - (void)addDigestAuthChallenge:(CFHTTPMessageRef)response
  379. {
  380. NSString *authFormat = @"Digest realm="%@", qop="auth", nonce="%@"";
  381. NSString *authInfo = [NSString stringWithFormat:authFormat, [self realm], [self generateNonce]];
  382. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("WWW-Authenticate"), (CFStringRef)authInfo);
  383. }
  384. /**
  385.  * Adds a basic authentication challenge to the given response.
  386. **/
  387. - (void)addBasicAuthChallenge:(CFHTTPMessageRef)response
  388. {
  389. NSString *authFormat = @"Basic realm="%@"";
  390. NSString *authInfo = [NSString stringWithFormat:authFormat, [self realm]];
  391. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("WWW-Authenticate"), (CFStringRef)authInfo);
  392. }
  393. /**
  394.  * Attempts to parse the given range header into a series of non-overlapping ranges.
  395.  * If successfull, the variables 'ranges' and 'rangeIndex' will be updated, and YES will be returned.
  396.  * Otherwise, NO is returned, and the range request should be ignored.
  397.  **/
  398. - (BOOL)parseRangeRequest:(NSString *)rangeHeader withContentLength:(UInt64)contentLength
  399. {
  400. // Examples of byte-ranges-specifier values (assuming an entity-body of length 10000):
  401. // 
  402. // - The first 500 bytes (byte offsets 0-499, inclusive):  bytes=0-499
  403. // 
  404. // - The second 500 bytes (byte offsets 500-999, inclusive): bytes=500-999
  405. // 
  406. // - The final 500 bytes (byte offsets 9500-9999, inclusive): bytes=-500
  407. // 
  408. // - Or bytes=9500-
  409. // 
  410. // - The first and last bytes only (bytes 0 and 9999):  bytes=0-0,-1
  411. // 
  412. // - Several legal but not canonical specifications of the second 500 bytes (byte offsets 500-999, inclusive):
  413. // bytes=500-600,601-999
  414. // bytes=500-700,601-999
  415. // 
  416. NSRange eqsignRange = [rangeHeader rangeOfString:@"="];
  417. if(eqsignRange.location == NSNotFound) return NO;
  418. NSUInteger tIndex = eqsignRange.location;
  419. NSUInteger fIndex = eqsignRange.location + eqsignRange.length;
  420. NSString *rangeType  = [[[rangeHeader substringToIndex:tIndex] mutableCopy] autorelease];
  421. NSString *rangeValue = [[[rangeHeader substringFromIndex:fIndex] mutableCopy] autorelease];
  422. CFStringTrimWhitespace((CFMutableStringRef)rangeType);
  423. CFStringTrimWhitespace((CFMutableStringRef)rangeValue);
  424. if([rangeType caseInsensitiveCompare:@"bytes"] != NSOrderedSame) return NO;
  425. NSArray *rangeComponents = [rangeValue componentsSeparatedByString:@","];
  426. if([rangeComponents count] == 0) return NO;
  427. [ranges release];
  428. ranges = [[NSMutableArray alloc] initWithCapacity:[rangeComponents count]];
  429. rangeIndex = 0;
  430. // Note: We store all range values in the form of NSRange structs, wrapped in NSValue objects.
  431. // Since NSRange consists of NSUInteger values, the range is limited to 4 gigs on 32-bit architectures (ppc, i386)
  432. NSUInteger i;
  433. for(i = 0; i < [rangeComponents count]; i++)
  434. {
  435. NSString *rangeComponent = [rangeComponents objectAtIndex:i];
  436. NSRange dashRange = [rangeComponent rangeOfString:@"-"];
  437. if(dashRange.location == NSNotFound)
  438. {
  439. // We're dealing with an individual byte number
  440. UInt64 byteIndex;
  441. if(![NSNumber parseString:rangeComponent intoUInt64:&byteIndex]) return NO;
  442. [ranges addObject:[NSValue valueWithDDRange:DDMakeRange(byteIndex, 1)]];
  443. }
  444. else
  445. {
  446. // We're dealing with a range of bytes
  447. tIndex = dashRange.location;
  448. fIndex = dashRange.location + dashRange.length;
  449. NSString *r1str = [rangeComponent substringToIndex:tIndex];
  450. NSString *r2str = [rangeComponent substringFromIndex:fIndex];
  451. UInt64 r1, r2;
  452. BOOL hasR1 = [NSNumber parseString:r1str intoUInt64:&r1];
  453. BOOL hasR2 = [NSNumber parseString:r2str intoUInt64:&r2];
  454. if(!hasR1)
  455. {
  456. // We're dealing with a "-[#]" range
  457. // 
  458. // r2 is the number of ending bytes to include in the range
  459. if(!hasR2) return NO;
  460. if(r2 > contentLength) return NO;
  461. UInt64 startIndex = contentLength - r2;
  462. [ranges addObject:[NSValue valueWithDDRange:DDMakeRange(startIndex, r2)]];
  463. }
  464. else if(!hasR2)
  465. {
  466. // We're dealing with a "[#]-" range
  467. // 
  468. // r1 is the starting index of the range, which goes all the way to the end
  469. if(!hasR1) return NO;
  470. if(r1 >= contentLength) return NO;
  471. [ranges addObject:[NSValue valueWithDDRange:DDMakeRange(r1, contentLength - r1)]];
  472. }
  473. else
  474. {
  475. // We're dealing with a normal "[#]-[#]" range
  476. // 
  477. // Note: The range is inclusive. So 0-1 has a length of 2 bytes.
  478. if(!hasR1) return NO;
  479. if(!hasR2) return NO;
  480. if(r1 > r2) return NO;
  481. [ranges addObject:[NSValue valueWithDDRange:DDMakeRange(r1, r2 - r1 + 1)]];
  482. }
  483. }
  484. }
  485. if([ranges count] == 0) return NO;
  486. for(i = 0; i < [ranges count] - 1; i++)
  487. {
  488. DDRange range1 = [[ranges objectAtIndex:i] ddrangeValue];
  489. NSUInteger j;
  490. for(j = i+1; j < [ranges count]; j++)
  491. {
  492. DDRange range2 = [[ranges objectAtIndex:j] ddrangeValue];
  493. DDRange iRange = DDIntersectionRange(range1, range2);
  494. if(iRange.length != 0)
  495. {
  496. return NO;
  497. }
  498. }
  499. }
  500. return YES;
  501. }
  502. /**
  503.  * Gets the current date and time, formatted properly (according to RFC) for insertion into an HTTP header.
  504. **/
  505. - (NSString *)dateAsString:(NSDate *)date
  506. {
  507. // Example: Sun, 06 Nov 1994 08:49:37 GMT
  508. NSDateFormatter *df = [[[NSDateFormatter alloc] init] autorelease];
  509. [df setFormatterBehavior:NSDateFormatterBehavior10_4];
  510. [df setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
  511. [df setDateFormat:@"EEE, dd MMM y hh:mm:ss 'GMT'"];
  512. // For some reason, using zzz in the format string produces GMT+00:00
  513. return [df stringFromDate:date];
  514. }
  515. /**
  516.  * This method is called after a full HTTP request has been received.
  517.  * The current request is in the CFHTTPMessage request variable.
  518.  **/
  519. - (void)replyToHTTPRequest
  520. {
  521. // Check the HTTP version - if it's anything but HTTP version 1.1, we don't support it
  522. NSString *version = [(NSString *)CFHTTPMessageCopyVersion(request) autorelease];
  523. if(!version || ![version isEqualToString:(NSString *)kCFHTTPVersion1_1])
  524. {
  525. [self handleVersionNotSupported:version];
  526. return;
  527. }
  528. // Check HTTP method
  529. NSString *method = [(NSString *)CFHTTPMessageCopyRequestMethod(request) autorelease];
  530.     if(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"] && ![method isEqualToString:@"POST"])
  531. {
  532. [self handleUnknownMethod:method];
  533.         return;
  534.     }
  535. // Extract requested URI
  536. NSURL *uri = [(NSURL *)CFHTTPMessageCopyRequestURL(request) autorelease];
  537. // Check Authentication (if needed)
  538. // If not properly authenticated for resource, issue Unauthorized response
  539. if([self isPasswordProtected:[uri relativeString]] && ![self isAuthenticated])
  540. {
  541. [self handleAuthenticationFailed];
  542. return;
  543. }
  544. // Respond properly to HTTP 'GET' and 'HEAD' commands
  545. httpResponse = [[self httpResponseForURI:[uri relativeString]] retain];
  546. UInt64 contentLength = [httpResponse contentLength];
  547. if(contentLength == 0)
  548. {
  549. [self handleResourceNotFound];
  550. [httpResponse release];
  551. httpResponse = nil;
  552. return;
  553.     }
  554. // Check for specific range request
  555. NSString *rangeHeader = [(NSString *)CFHTTPMessageCopyHeaderFieldValue(request, CFSTR("Range")) autorelease];
  556. BOOL isRangeRequest = NO;
  557. if(rangeHeader)
  558. {
  559. if([self parseRangeRequest:rangeHeader withContentLength:contentLength])
  560. {
  561. isRangeRequest = YES;
  562. }
  563. }
  564. CFHTTPMessageRef response;
  565. if(!isRangeRequest)
  566. {
  567. // Status Code 200 - OK
  568. response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1);
  569. NSString *contentLengthStr = [NSString stringWithFormat:@"%qu", contentLength];
  570. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), (CFStringRef)contentLengthStr);
  571. }
  572. else
  573. {
  574. if([ranges count] == 1)
  575. {
  576. response = [self prepareUniRangeResponse:contentLength];
  577. }
  578. else
  579. {
  580. response = [self prepareMultiRangeResponse:contentLength];
  581. }
  582. }
  583.     
  584. // If they issue a 'HEAD' command, we don't have to include the file
  585. // If they issue a 'GET' command, we need to include the file
  586. if([method isEqual:@"HEAD"])
  587. {
  588. NSData *responseData = [self preprocessResponse:response];
  589. [asyncSocket writeData:responseData withTimeout:WRITE_HEAD_TIMEOUT tag:HTTP_RESPONSE];
  590. }
  591. else
  592. {
  593. // Write the header response
  594. NSData *responseData = [self preprocessResponse:response];
  595. [asyncSocket writeData:responseData withTimeout:WRITE_HEAD_TIMEOUT tag:HTTP_PARTIAL_RESPONSE_HEADER];
  596. // Now we need to send the body of the response
  597. if(!isRangeRequest)
  598. {
  599. // Regular request
  600. NSData *data = [httpResponse readDataOfLength:READ_CHUNKSIZE];
  601. [asyncSocket writeData:data withTimeout:WRITE_BODY_TIMEOUT tag:HTTP_PARTIAL_RESPONSE_BODY];
  602. }
  603. else
  604. {
  605. // Client specified a byte range in request
  606. if([ranges count] == 1)
  607. {
  608. // Client is requesting a single range
  609. DDRange range = [[ranges objectAtIndex:0] ddrangeValue];
  610. [httpResponse setOffset:range.location];
  611. unsigned int bytesToRead = range.length < READ_CHUNKSIZE ? range.length : READ_CHUNKSIZE;
  612. NSData *data = [httpResponse readDataOfLength:bytesToRead];
  613. [asyncSocket writeData:data withTimeout:WRITE_BODY_TIMEOUT tag:HTTP_PARTIAL_RANGE_RESPONSE_BODY];
  614. }
  615. else
  616. {
  617. // Client is requesting multiple ranges
  618. // We have to send each range using multipart/byteranges
  619. // Write range header
  620. NSData *rangeHeader = [ranges_headers objectAtIndex:0];
  621. [asyncSocket writeData:rangeHeader withTimeout:WRITE_HEAD_TIMEOUT tag:HTTP_PARTIAL_RESPONSE_HEADER];
  622. // Start writing range body
  623. DDRange range = [[ranges objectAtIndex:0] ddrangeValue];
  624. [httpResponse setOffset:range.location];
  625. unsigned int bytesToRead = range.length < READ_CHUNKSIZE ? range.length : READ_CHUNKSIZE;
  626. NSData *data = [httpResponse readDataOfLength:bytesToRead];
  627. [asyncSocket writeData:data withTimeout:WRITE_BODY_TIMEOUT tag:HTTP_PARTIAL_RANGES_RESPONSE_BODY];
  628. }
  629. }
  630. }
  631. CFRelease(response);
  632. }
  633. /**
  634.  * Prepares a single-range response.
  635.  * 
  636.  * Note: The returned CFHTTPMessageRef is owned by the sender, who is responsible for releasing it.
  637. **/
  638. - (CFHTTPMessageRef)prepareUniRangeResponse:(UInt64)contentLength
  639. {
  640. // Status Code 206 - Partial Content
  641. CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 206, NULL, kCFHTTPVersion1_1);
  642. DDRange range = [[ranges objectAtIndex:0] ddrangeValue];
  643. NSString *contentLengthStr = [NSString stringWithFormat:@"%qu", range.length];
  644. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), (CFStringRef)contentLengthStr);
  645. NSString *rangeStr = [NSString stringWithFormat:@"%qu-%qu", range.location, DDMaxRange(range) - 1];
  646. NSString *contentRangeStr = [NSString stringWithFormat:@"bytes %@/%qu", rangeStr, contentLength];
  647. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Range"), (CFStringRef)contentRangeStr);
  648. return response;
  649. }
  650. /**
  651.  * Prepares a multi-range response.
  652.  * 
  653.  * Note: The returned CFHTTPMessageRef is owned by the sender, who is responsible for releasing it.
  654. **/
  655. - (CFHTTPMessageRef)prepareMultiRangeResponse:(UInt64)contentLength
  656. {
  657. // Status Code 206 - Partial Content
  658. CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 206, NULL, kCFHTTPVersion1_1);
  659. // We have to send each range using multipart/byteranges
  660. // So each byterange has to be prefix'd and suffix'd with the boundry
  661. // Example:
  662. // 
  663. // HTTP/1.1 206 Partial Content
  664. // Content-Length: 220
  665. // Content-Type: multipart/byteranges; boundary=4554d24e986f76dd6
  666. // 
  667. // 
  668. // --4554d24e986f76dd6
  669. // Content-range: bytes 0-25/4025
  670. // 
  671. // [...]
  672. // --4554d24e986f76dd6
  673. // Content-range: bytes 3975-4024/4025
  674. // 
  675. // [...]
  676. // --4554d24e986f76dd6--
  677. ranges_headers = [[NSMutableArray alloc] initWithCapacity:[ranges count]];
  678. CFUUIDRef theUUID = CFUUIDCreate(NULL);
  679. ranges_boundry = (NSString *)CFUUIDCreateString(NULL, theUUID);
  680. CFRelease(theUUID);
  681. NSString *startingBoundryStr = [NSString stringWithFormat:@"rn--%@rn", ranges_boundry];
  682. NSString *endingBoundryStr = [NSString stringWithFormat:@"rn--%@--rn", ranges_boundry];
  683. UInt64 actualContentLength = 0;
  684. unsigned i;
  685. for(i = 0; i < [ranges count]; i++)
  686. {
  687. DDRange range = [[ranges objectAtIndex:i] ddrangeValue];
  688. NSString *rangeStr = [NSString stringWithFormat:@"%qu-%qu", range.location, DDMaxRange(range) - 1];
  689. NSString *contentRangeVal = [NSString stringWithFormat:@"bytes %@/%qu", rangeStr, contentLength];
  690. NSString *contentRangeStr = [NSString stringWithFormat:@"Content-Range: %@rnrn", contentRangeVal];
  691. NSString *fullHeader = [startingBoundryStr stringByAppendingString:contentRangeStr];
  692. NSData *fullHeaderData = [fullHeader dataUsingEncoding:NSUTF8StringEncoding];
  693. [ranges_headers addObject:fullHeaderData];
  694. actualContentLength += [fullHeaderData length];
  695. actualContentLength += range.length;
  696. }
  697. NSData *endingBoundryData = [endingBoundryStr dataUsingEncoding:NSUTF8StringEncoding];
  698. actualContentLength += [endingBoundryData length];
  699. NSString *contentLengthStr = [NSString stringWithFormat:@"%qu", actualContentLength];
  700. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), (CFStringRef)contentLengthStr);
  701. NSString *contentTypeStr = [NSString stringWithFormat:@"multipart/byteranges; boundary=%@", ranges_boundry];
  702. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), (CFStringRef)contentTypeStr);
  703. return response;
  704. }
  705. /**
  706.  * Converts relative URI path into full file-system path.
  707. **/
  708. - (NSString *)filePathForURI:(NSString *)path
  709. {
  710. // Override me to perform custom path mapping.
  711. // For example you may want to use a default file other than index.html, or perhaps support multiple types.
  712. // If there is no configured documentRoot, then it makes no sense to try to return anything
  713. if(![server documentRoot]) return nil;
  714. // Convert path to a relative path.
  715. // This essentially means trimming beginning '/' characters.
  716. // Beware of a bug in the Cocoa framework:
  717. // 
  718. // [NSURL URLWithString:@"/foo" relativeToURL:baseURL]       == @"/baseURL/foo"
  719. // [NSURL URLWithString:@"/foo%20bar" relativeToURL:baseURL] == @"/foo bar"
  720. // [NSURL URLWithString:@"/foo" relativeToURL:baseURL]       == @"/foo"
  721. NSString *relativePath = path;
  722. while([relativePath hasPrefix:@"/"] && [relativePath length] > 1)
  723. {
  724. relativePath = [relativePath substringFromIndex:1];
  725. }
  726. NSURL *url;
  727. if([relativePath hasSuffix:@"/"])
  728. {
  729. NSString *completedRelativePath = [relativePath stringByAppendingString:@"index.html"];
  730. url = [NSURL URLWithString:completedRelativePath relativeToURL:[server documentRoot]];
  731. }
  732. else
  733. {
  734. url = [NSURL URLWithString:relativePath relativeToURL:[server documentRoot]];
  735. }
  736. // Watch out for sneaky requests with ".." in the path
  737. // For example, the following request: "../Documents/TopSecret.doc"
  738. if(![[url path] hasPrefix:[[server documentRoot] path]]) return nil;
  739. return [[url path] stringByStandardizingPath];
  740. }
  741. /**
  742.  * This method creates a html browseable page.
  743.  * Customize to fit your needs
  744.  **/
  745. - (NSString *) createBrowseableIndex:(NSString *)path
  746. {
  747.     NSArray *array = [[NSFileManager defaultManager] directoryContentsAtPath:path];
  748.     
  749.     NSMutableString *outdata = [NSMutableString new];
  750. [outdata appendString:@"<html><head>"];
  751. [outdata appendFormat:@"<title>Files from %@</title>", server.name];
  752.     [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>"];
  753.     [outdata appendString:@"</head><body>"];
  754. [outdata appendFormat:@"<h1>Files from %@</h1>", server.name];
  755.     [outdata appendString:@"<bq>The following files are hosted live from the iPhone's Docs folder.</bq>"];
  756.     [outdata appendString:@"<p>"];
  757. [outdata appendFormat:@"<a href="..">..</a><br />n"];
  758.     for (NSString *fname in array)
  759.     {
  760.         NSDictionary *fileDict = [[NSFileManager defaultManager] fileAttributesAtPath:[path stringByAppendingPathComponent:fname] traverseLink:NO];
  761. //NSLog(@"fileDict: %@", fileDict);
  762.         NSString *modDate = [[fileDict objectForKey:NSFileModificationDate] description];
  763. if ([[fileDict objectForKey:NSFileType] isEqualToString: @"NSFileTypeDirectory"]) fname = [fname stringByAppendingString:@"/"];
  764. [outdata appendFormat:@"<a href="%@">%@</a> (%8.1f Kb, %@)<br />n", fname, fname, [[fileDict objectForKey:NSFileSize] floatValue] / 1024, modDate];
  765.     }
  766.     [outdata appendString:@"</p>"];
  767. if ([self supportsPOST:path withSize:0])
  768. {
  769. [outdata appendString:@"<form action="" method="post" enctype="multipart/form-data" name="form1" id="form1">"];
  770. [outdata appendString:@"<label>upload file"];
  771. [outdata appendString:@"<input type="file" name="file" id="file" />"];
  772. [outdata appendString:@"</label>"];
  773. [outdata appendString:@"<label>"];
  774. [outdata appendString:@"<input type="submit" name="button" id="button" value="Submit" />"];
  775. [outdata appendString:@"</label>"];
  776. [outdata appendString:@"</form>"];
  777. }
  778. [outdata appendString:@"</body></html>"];
  779.     
  780. //NSLog(@"outData: %@", outdata);
  781.     return [outdata autorelease];
  782. }
  783. /**
  784.  * This method is called to get a response for a request.
  785.  * You may return any object that adopts the HTTPResponse protocol.
  786.  * The HTTPServer comes with two such classes: HTTPFileResponse and HTTPDataResponse.
  787.  * HTTPFileResponse is a wrapper for an NSFileHandle object, and is the preferred way to send a file response.
  788.  * HTTPDataResopnse is a wrapper for an NSData object, and may be used to send a custom response.
  789. **/
  790. - (NSObject<HTTPResponse> *)httpResponseForURI:(NSString *)path
  791. {
  792. //NSLog(@"httpResponseForURI: %@", path);
  793. if (postContentLength > 0) //process POST data
  794. {
  795. NSLog(@"processing post data: %i", postContentLength);
  796. UInt16 separatorBytes = 0x0A0D;
  797. NSMutableData* separatorData = [NSMutableData dataWithBytes:&separatorBytes length:2];
  798. [separatorData appendData:[multipartData objectAtIndex:0]];
  799. int l = [separatorData length];
  800. int count = 2; //number of times the separator shows up at the end of file data
  801. NSFileHandle* dataToTrim = [multipartData lastObject];
  802. NSLog(@"data: %@", dataToTrim);
  803. for (unsigned long long i = [dataToTrim offsetInFile] - l; i > 0; i--)
  804. {
  805. [dataToTrim seekToFileOffset:i];
  806. if ([[dataToTrim readDataOfLength:l] isEqualToData:separatorData])
  807. {
  808. [dataToTrim truncateFileAtOffset:i];
  809. i -= l;
  810. if (--count == 0) break;
  811. }
  812. }
  813. NSLog(@"NewFileUploaded");
  814. //Set up notification, u can use [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refresh) name:@"NewFileUploaded" object:nil] from anywhere to receive the notfication
  815. [[NSNotificationCenter defaultCenter] postNotificationName:@"NewFileUploaded" object:nil];
  816. for (int n = 1; n < [multipartData count] - 1; n++)
  817. NSLog(@"%@", [[NSString alloc] initWithBytes:[[multipartData objectAtIndex:n] bytes] length:[[multipartData objectAtIndex:n] length] encoding:NSUTF8StringEncoding]);
  818. [multipartData release];
  819. postContentLength = 0;
  820. }
  821. NSString *filePath = [self filePathForURI:path];
  822. if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
  823. {
  824. return [[[HTTPFileResponse alloc] initWithFilePath:filePath] autorelease];
  825. }
  826. else //no 
  827. {
  828. NSString *folder = [path isEqualToString:@"/"] ? [[server documentRoot] path] : [NSString stringWithFormat: @"%@%@", [[server documentRoot] path], path];
  829. if ([self isBrowseable:folder])
  830. {
  831. //NSLog(@"folder: %@", folder);
  832. NSData *browseData = [[self createBrowseableIndex:folder] dataUsingEncoding:NSUTF8StringEncoding];
  833. return [[[HTTPDataResponse alloc] initWithData:browseData] autorelease];
  834. }
  835. }
  836. return nil;
  837. }
  838. /**
  839.  * This method is called to handle data read from a POST.
  840.  * The given data is part of the POST body.
  841. **/
  842. - (void)processPostDataChunk:(NSData *)postDataChunk
  843. {
  844. //NSLog(@"processPostDataChunk");
  845. if (!postHeaderOK)
  846. {
  847. UInt16 separatorBytes = 0x0A0D;
  848. NSData* separatorData = [NSData dataWithBytes:&separatorBytes length:2];
  849. int l = [separatorData length];
  850. for (int i = 0; i < [postDataChunk length] - l; i++)
  851. {
  852. NSRange searchRange = {i, l};
  853. if ([[postDataChunk subdataWithRange:searchRange] isEqualToData:separatorData])
  854. {
  855. NSRange newDataRange = {dataStartIndex, i - dataStartIndex};
  856. dataStartIndex = i + l;
  857. i += l - 1;
  858. NSData *newData = [postDataChunk subdataWithRange:newDataRange];
  859. if ([newData length])
  860. {
  861. [multipartData addObject:newData];
  862. }
  863. else
  864. {
  865. postHeaderOK = TRUE;
  866. NSString* postInfo = [[NSString alloc] initWithBytes:[[multipartData objectAtIndex:1] bytes] length:[[multipartData objectAtIndex:1] length] encoding:NSUTF8StringEncoding];
  867. NSArray* postInfoComponents = [postInfo componentsSeparatedByString:@"; filename="];
  868. postInfoComponents = [[postInfoComponents lastObject] componentsSeparatedByString:@"""];
  869. postInfoComponents = [[postInfoComponents objectAtIndex:1] componentsSeparatedByString:@"\"];
  870. NSString* filename = [[[server documentRoot] path] stringByAppendingPathComponent:[postInfoComponents lastObject]];
  871. NSRange fileDataRange = {dataStartIndex, [postDataChunk length] - dataStartIndex};
  872. [[NSFileManager defaultManager] createFileAtPath:filename contents:[postDataChunk subdataWithRange:fileDataRange] attributes:nil];
  873. NSFileHandle *file = [[NSFileHandle fileHandleForUpdatingAtPath:filename] retain];
  874. [file seekToEndOfFile];
  875. [multipartData addObject:file];
  876. [postInfo release];
  877. /*
  878. NSRange fileDataRange = {dataStartIndex, [postDataChunk length] - dataStartIndex};
  879. NSMutableData *fileData = [NSMutableData dataWithData:[postDataChunk subdataWithRange:fileDataRange]];
  880. [multipartData addObject:fileData];
  881.  */
  882. break;
  883. }
  884. }
  885. }
  886. }
  887. else
  888. {
  889. NSLog(@"file: %@", [multipartData lastObject]);
  890. [(NSFileHandle*)[multipartData lastObject] writeData:postDataChunk];
  891. //[[multipartData lastObject] appendData:postDataChunk];
  892. }
  893. // Override me to do something useful with a POST.
  894. // If the post is small, such as a simple form, you may want to simply append the data to the request.
  895. // If the post is big, such as a file upload, you may want to store the file to disk.
  896. // 
  897. // Remember: In order to support LARGE POST uploads, the data is read in chunks.
  898. // This prevents a 50 MB upload from being stored in RAM.
  899. // The size of the chunks are limited by the POST_CHUNKSIZE definition.
  900. // Therefore, this method may be called multiple times for the same POST request.
  901. }
  902. /**
  903.  * Called if the HTML version is other than what is supported
  904. **/
  905. - (void)handleVersionNotSupported:(NSString *)version
  906. {
  907. // Override me for custom error handling of unspupported http version responses
  908. // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
  909. // You can also use preprocessErrorResponse: to add an optional HTML body.
  910. //NSLog(@"HTTP Server: Error 505 - Version Not Supported: %@", version);
  911. CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 505, NULL, (CFStringRef)version);
  912. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), CFSTR("0"));
  913.     
  914. NSData *responseData = [self preprocessErrorResponse:response];
  915. [asyncSocket writeData:responseData withTimeout:WRITE_ERROR_TIMEOUT tag:HTTP_RESPONSE];
  916. CFRelease(response);
  917. }
  918. /**
  919.  * Called if the authentication information was required and absent, or if authentication failed.
  920. **/
  921. - (void)handleAuthenticationFailed
  922. {
  923. // Override me for custom handling of authentication challenges
  924. // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
  925. // You can also use preprocessErrorResponse: to add an optional HTML body.
  926. //NSLog(@"HTTP Server: Error 401 - Unauthorized");
  927. // Status Code 401 - Unauthorized
  928. CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 401, NULL, kCFHTTPVersion1_1);
  929. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), CFSTR("0"));
  930. if([self useDigestAccessAuthentication])
  931. {
  932. [self addDigestAuthChallenge:response];
  933. }
  934. else
  935. {
  936. [self addBasicAuthChallenge:response];
  937. }
  938. NSData *responseData = [self preprocessErrorResponse:response];
  939. [asyncSocket writeData:responseData withTimeout:WRITE_ERROR_TIMEOUT tag:HTTP_RESPONSE];
  940. CFRelease(response);
  941. }
  942. /**
  943.  * Called if we receive some sort of malformed HTTP request.
  944.  * The data parameter is the invalid HTTP header line, including CRLF, as read from AsyncSocket.
  945.  * The data parameter may also be nil if the request as a whole was invalid, such as a POST with no Content-Length.
  946. **/
  947. - (void)handleInvalidRequest:(NSData *)data
  948. {
  949. // Override me for custom error handling of invalid HTTP requests
  950. // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
  951. // You can also use preprocessErrorResponse: to add an optional HTML body.
  952. //NSLog(@"HTTP Server: Error 400 - Bad Request");
  953. // Status Code 400 - Bad Request
  954. CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 400, NULL, kCFHTTPVersion1_1);
  955. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), CFSTR("0"));
  956. NSData *responseData = [self preprocessErrorResponse:response];
  957. [asyncSocket writeData:responseData withTimeout:WRITE_ERROR_TIMEOUT tag:HTTP_FINAL_RESPONSE];
  958. CFRelease(response);
  959. }
  960. /**
  961.  * Called if we receive a HTTP request with a method other than GET or HEAD.
  962. **/
  963. - (void)handleUnknownMethod:(NSString *)method
  964. {
  965. // Override me to add support for methods other than GET and HEAD
  966. // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
  967. // You can also use preprocessErrorResponse: to add an optional HTML body.
  968. //NSLog(@"HTTP Server: Error 405 - Method Not Allowed: %@", method);
  969. // Status code 405 - Method Not Allowed
  970.     CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 405, NULL, kCFHTTPVersion1_1);
  971. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), CFSTR("0"));
  972. NSData *responseData = [self preprocessErrorResponse:response];
  973. [asyncSocket writeData:responseData withTimeout:WRITE_ERROR_TIMEOUT tag:HTTP_FINAL_RESPONSE];
  974.     
  975. CFRelease(response);
  976. // Note: We used the HTTP_FINAL_RESPONSE tag to disconnect after the response is sent.
  977. // We do this because the method may include an http body.
  978. // Since we can't be sure, we should close the connection.
  979. }
  980. - (void)handleResourceNotFound
  981. {
  982. // Override me for custom error handling of 404 not found responses
  983. // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
  984. // You can also use preprocessErrorResponse: to add an optional HTML body.
  985. //NSLog(@"HTTP Server: Error 404 - Not Found");
  986. // Status Code 404 - Not Found
  987. CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 404, NULL, kCFHTTPVersion1_1);
  988. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), CFSTR("0"));
  989. NSData *responseData = [self preprocessErrorResponse:response];
  990. [asyncSocket writeData:responseData withTimeout:WRITE_ERROR_TIMEOUT tag:HTTP_RESPONSE];
  991. CFRelease(response);
  992. }
  993. /**
  994.  * This method is called immediately prior to sending the response headers.
  995.  * This method adds standard header fields, and then converts the response to an NSData object.
  996. **/
  997. - (NSData *)preprocessResponse:(CFHTTPMessageRef)response
  998. {
  999. // Override me to customize the response headers
  1000. // You'll likely want to add your own custom headers, and then return [super preprocessResponse:response]
  1001. NSString *now = [self dateAsString:[NSDate date]];
  1002. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Date"), (CFStringRef)now);
  1003. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Accept-Ranges"), CFSTR("bytes"));
  1004. NSData *result = (NSData *)CFHTTPMessageCopySerializedMessage(response);
  1005. return [result autorelease];
  1006. }
  1007. /**
  1008.  * This method is called immediately prior to sending the response headers (for an error).
  1009.  * This method adds standard header fields, and then converts the response to an NSData object.
  1010. **/
  1011. - (NSData *)preprocessErrorResponse:(CFHTTPMessageRef)response;
  1012. {
  1013. // Override me to customize the error response headers
  1014. // You'll likely want to add your own custom headers, and then return [super preprocessErrorResponse:response]
  1015. // 
  1016. // Notes:
  1017. // You can use CFHTTPMessageGetResponseStatusCode(response) to get the type of error.
  1018. // You can use CFHTTPMessageSetBody() to add an optional HTML body.
  1019. // If you add a body, don't forget to update the Content-Length.
  1020. // 
  1021. // if(CFHTTPMessageGetResponseStatusCode(response) == 404)
  1022. // {
  1023. //     NSString *msg = @"<html><body>Error 404 - Not Found</body></html>";
  1024. //     NSData *msgData = [msg dataUsingEncoding:NSUTF8StringEncoding];
  1025. //     
  1026. //     CFHTTPMessageSetBody(response, (CFDataRef)msgData);
  1027. //     
  1028. //     NSString *contentLengthStr = [NSString stringWithFormat:@"%u", (unsigned)[msgData length]];
  1029. //     CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), (CFStringRef)contentLengthStr);
  1030. // }
  1031. NSString *now = [self dateAsString:[NSDate date]];
  1032. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Date"), (CFStringRef)now);
  1033. CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Accept-Ranges"), CFSTR("bytes"));
  1034. NSData *result = (NSData *)CFHTTPMessageCopySerializedMessage(response);
  1035. return [result autorelease];
  1036. }
  1037. - (void)die
  1038. {
  1039. // Post notification of dead connection
  1040. // This will allow our server to release us from its array of connections
  1041. [[NSNotificationCenter defaultCenter] postNotificationName:HTTPConnectionDidDieNotification object:self];
  1042. }
  1043. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1044. #pragma mark AsyncSocket Delegate Methods:
  1045. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1046. /**
  1047.  * This method is called immediately prior to opening up the stream.
  1048.  * This is the time to manually configure the stream if necessary.
  1049. **/
  1050. - (BOOL)onSocketWillConnect:(AsyncSocket *)sock
  1051. {
  1052. if([self isSecureServer])
  1053. {
  1054. NSArray *certificates = [self sslIdentityAndCertificates];
  1055. if([certificates count] > 0)
  1056. {
  1057. //NSLog(@"Securing connection...");
  1058. // All connections are assumed to be secure. Only secure connections are allowed on this server.
  1059. NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3];
  1060. // Configure this connection as the server
  1061. CFDictionaryAddValue((CFMutableDictionaryRef)settings,
  1062.  kCFStreamSSLIsServer, kCFBooleanTrue);
  1063. CFDictionaryAddValue((CFMutableDictionaryRef)settings,
  1064.  kCFStreamSSLCertificates, (CFArrayRef)certificates);
  1065. // Configure this connection to use the highest possible SSL level
  1066. CFDictionaryAddValue((CFMutableDictionaryRef)settings,
  1067.  kCFStreamSSLLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
  1068. CFReadStreamSetProperty([asyncSocket getCFReadStream],
  1069. kCFStreamPropertySSLSettings, (CFDictionaryRef)settings);
  1070. CFWriteStreamSetProperty([asyncSocket getCFWriteStream],
  1071.  kCFStreamPropertySSLSettings, (CFDictionaryRef)settings);
  1072. }
  1073. }
  1074. return YES;
  1075. }
  1076. /**
  1077.  * This method is called after the socket has successfully read data from the stream.
  1078.  * Remember that this method will only be called after the socket reaches a CRLF, or after it's read the proper length.
  1079. **/
  1080. - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag
  1081. {
  1082. if(tag == HTTP_REQUEST_HEADER)
  1083. {
  1084. // Append the header line to the http message
  1085. BOOL result = CFHTTPMessageAppendBytes(request, [data bytes], [data length]);
  1086. if(!result)
  1087. {
  1088. // We have a received a malformed request
  1089. [self handleInvalidRequest:data];
  1090. }
  1091. else if(!CFHTTPMessageIsHeaderComplete(request))
  1092. {
  1093. // We don't have a complete header yet
  1094. // That is, we haven't yet received a CRLF on a line by itself, indicating the end of the header
  1095. if(++numHeaderLines > LIMIT_MAX_HEADER_LINES)
  1096. {
  1097. // Reached the maximum amount of header lines in a single HTTP request
  1098. // This could be an attempted DOS attack
  1099. [asyncSocket disconnect];
  1100. }
  1101. else
  1102. {
  1103. [asyncSocket readDataToData:[AsyncSocket CRLFData]
  1104. withTimeout:READ_TIMEOUT
  1105.   maxLength:LIMIT_MAX_HEADER_LINE_LENGTH
  1106. tag:HTTP_REQUEST_HEADER];
  1107. }
  1108. }
  1109. else
  1110. {
  1111. // We have an entire HTTP request header from the client
  1112. // Check to see if it's a POST (upload) request
  1113. NSString *method = [(NSString *)CFHTTPMessageCopyRequestMethod(request) autorelease];
  1114. if([method isEqualToString:@"POST"])
  1115. {
  1116. NSURL *uri = [(NSURL *)CFHTTPMessageCopyRequestURL(request) autorelease];
  1117. NSString *contentLengthStr =
  1118.     [(NSString *)CFHTTPMessageCopyHeaderFieldValue(request, CFSTR("Content-Length")) autorelease];
  1119. if(contentLengthStr == nil)
  1120. {
  1121. // Received POST with no specified Content-Length
  1122. [self handleInvalidRequest:nil];
  1123. return;
  1124. }
  1125. if(![NSNumber parseString:(NSString *)contentLengthStr intoUInt64:&postContentLength])
  1126. {
  1127. // Unable to parse Content-Length header into a valid number
  1128. [self handleInvalidRequest:nil];
  1129. return;
  1130. }
  1131. if(![self supportsPOST:[uri relativeString] withSize:postContentLength])
  1132. {
  1133. // POST is unsupported - either in general, or for this specific request
  1134. // Send a 405 - Method not allowed response
  1135. [self handleUnknownMethod:method];
  1136. return;
  1137. }
  1138. // Reset the total amount of data received for the POST
  1139. postTotalBytesReceived = 0;
  1140. // Start reading the POST body
  1141. unsigned int bytesToRead = postContentLength < POST_CHUNKSIZE ? postContentLength : POST_CHUNKSIZE;
  1142. [asyncSocket readDataToLength:bytesToRead withTimeout:READ_TIMEOUT tag:HTTP_REQUEST_BODY];
  1143. }
  1144. else
  1145. {
  1146. // Now we need to reply to the request
  1147. [self replyToHTTPRequest];
  1148. }
  1149. }
  1150. }
  1151. else
  1152. {
  1153. // Handle a chunk of data from the POST body
  1154. postTotalBytesReceived += [data length];
  1155. [self processPostDataChunk:data];
  1156. if(postTotalBytesReceived < postContentLength)
  1157. {
  1158. // We're not done reading the post body yet...
  1159. UInt64 bytesLeft = postContentLength - postTotalBytesReceived;
  1160. unsigned int bytesToRead = bytesLeft < POST_CHUNKSIZE ? bytesLeft : POST_CHUNKSIZE;
  1161. [asyncSocket readDataToLength:bytesToRead withTimeout:READ_TIMEOUT tag:HTTP_REQUEST_BODY];
  1162. }
  1163. else
  1164. {
  1165. // Now we need to reply to the request
  1166. [self replyToHTTPRequest];
  1167. }
  1168. }
  1169. }
  1170. /**
  1171.  * This method is called after the socket has successfully written data to the stream.
  1172.  * Remember that this method will be called after a complete response to a request has been written.
  1173. **/
  1174. - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
  1175. {
  1176. BOOL doneSendingResponse = NO;
  1177. if(tag == HTTP_PARTIAL_RESPONSE_BODY)
  1178. {
  1179. // We only wrote a part of the http response - there may be more.
  1180. NSData *data = [httpResponse readDataOfLength:READ_CHUNKSIZE];
  1181. if([data length] > 0)
  1182. {
  1183. [asyncSocket writeData:data withTimeout:WRITE_BODY_TIMEOUT tag:tag];
  1184. }
  1185. else
  1186. {
  1187. doneSendingResponse = YES;
  1188. }
  1189. }
  1190. else if(tag == HTTP_PARTIAL_RANGE_RESPONSE_BODY)
  1191. {
  1192. // We only wrote a part of the range - there may be more.
  1193. DDRange range = [[ranges objectAtIndex:0] ddrangeValue];
  1194. UInt64 offset = [httpResponse offset];
  1195. UInt64 bytesRead = offset - range.location;
  1196. UInt64 bytesLeft = range.length - bytesRead;
  1197. if(bytesLeft > 0)
  1198. {
  1199. unsigned int bytesToRead = bytesLeft < READ_CHUNKSIZE ? bytesLeft : READ_CHUNKSIZE;
  1200. NSData *data = [httpResponse readDataOfLength:bytesToRead];
  1201. [asyncSocket writeData:data withTimeout:WRITE_BODY_TIMEOUT tag:tag];
  1202. }
  1203. else
  1204. {
  1205. doneSendingResponse = YES;
  1206. }
  1207. }
  1208. else if(tag == HTTP_PARTIAL_RANGES_RESPONSE_BODY)
  1209. {
  1210. // We only wrote part of the range - there may be more.
  1211. // Plus, there may be more ranges.
  1212. DDRange range = [[ranges objectAtIndex:rangeIndex] ddrangeValue];
  1213. UInt64 offset = [httpResponse offset];
  1214. UInt64 bytesRead = offset - range.location;
  1215. UInt64 bytesLeft = range.length - bytesRead;
  1216. if(bytesLeft > 0)
  1217. {
  1218. unsigned int bytesToRead = bytesLeft < READ_CHUNKSIZE ? bytesLeft : READ_CHUNKSIZE;
  1219. NSData *data = [httpResponse readDataOfLength:bytesToRead];
  1220. [asyncSocket writeData:data withTimeout:WRITE_BODY_TIMEOUT tag:tag];
  1221. }
  1222. else
  1223. {
  1224. if(++rangeIndex < [ranges count])
  1225. {
  1226. // Write range header
  1227. NSData *rangeHeader = [ranges_headers objectAtIndex:rangeIndex];
  1228. [asyncSocket writeData:rangeHeader withTimeout:WRITE_HEAD_TIMEOUT tag:HTTP_PARTIAL_RESPONSE_HEADER];
  1229. // Start writing range body
  1230. range = [[ranges objectAtIndex:rangeIndex] ddrangeValue];
  1231. [httpResponse setOffset:range.location];
  1232. unsigned int bytesToRead = range.length < READ_CHUNKSIZE ? range.length : READ_CHUNKSIZE;
  1233. NSData *data = [httpResponse readDataOfLength:bytesToRead];
  1234. [asyncSocket writeData:data withTimeout:WRITE_BODY_TIMEOUT tag:tag];
  1235. }
  1236. else
  1237. {
  1238. // We're not done yet - we still have to send the closing boundry tag
  1239. NSString *endingBoundryStr = [NSString stringWithFormat:@"rn--%@--rn", ranges_boundry];
  1240. NSData *endingBoundryData = [endingBoundryStr dataUsingEncoding:NSUTF8StringEncoding];
  1241. [asyncSocket writeData:endingBoundryData withTimeout:WRITE_HEAD_TIMEOUT tag:HTTP_RESPONSE];
  1242. }
  1243. }
  1244. }
  1245. else if(tag == HTTP_RESPONSE || tag == HTTP_FINAL_RESPONSE)
  1246. {
  1247. doneSendingResponse = YES;
  1248. }
  1249. if(doneSendingResponse)
  1250. {
  1251. if(tag == HTTP_FINAL_RESPONSE)
  1252. {
  1253. // Terminate the connection
  1254. [asyncSocket disconnect];
  1255. }
  1256. else
  1257. {
  1258. // Cleanup after the last request
  1259. // And start listening for the next request
  1260. // Release any resources we no longer need
  1261. [httpResponse release];
  1262. httpResponse = nil;
  1263. [ranges release];
  1264. [ranges_headers release];
  1265. [ranges_boundry release];
  1266. ranges = nil;
  1267. ranges_headers = nil;
  1268. ranges_boundry = nil;
  1269. // Release the old request, and create a new one
  1270. if(request) CFRelease(request);
  1271. request = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, YES);
  1272. numHeaderLines = 0;
  1273. // And start listening for more requests
  1274. [asyncSocket readDataToData:[AsyncSocket CRLFData]
  1275. withTimeout:READ_TIMEOUT
  1276.   maxLength:LIMIT_MAX_HEADER_LINE_LENGTH
  1277. tag:HTTP_REQUEST_HEADER];
  1278. }
  1279. }
  1280. }
  1281. /**
  1282.  * This message is sent:
  1283.  *  - if there is an connection, time out, or other i/o error.
  1284.  *  - if the remote socket cleanly disconnects.
  1285.  *  - before the local socket is disconnected.
  1286. **/
  1287. - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
  1288. {
  1289. // //NSLog(@"HTTPConnection: onSocket:willDisconnectWithError: %@", err);
  1290. }
  1291. /**
  1292.  * Sent after the socket has been disconnected.
  1293. **/
  1294. - (void)onSocketDidDisconnect:(AsyncSocket *)sock
  1295. {
  1296. [self die];
  1297. }
  1298. @end