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

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