Bug Summary

File:Plugins/Bonjour/libezv/Simple HTTP Server/HTTPServer.m
Location:line 460, column 5
Description:Memory Leak
Code is compiled without garbage collection.

Annotated Source Code

1#import "AsyncSocket.h"
2#import "HTTPServer.h"
3#import "HTTPAuthenticationRequest.h"
4#import <SystemConfiguration/SystemConfiguration.h>
5
6#import <stdlib.h>
7
8// Define the various timeouts (in seconds) for various parts of the HTTP process
9#define READ_TIMEOUT -1
10#define WRITE_HEAD_TIMEOUT 30
11#define WRITE_BODY_TIMEOUT -1
12#define WRITE_ERROR_TIMEOUT 30
13
14// Define the various tags we'll use to differentiate what it is we're currently doing
15#define HTTP_REQUEST 15
16#define HTTP_PARTIAL_RESPONSE 29
17#define HTTP_RESPONSE 30
18
19#define HTTPConnectionDidDieNotification @"HTTPConnectionDidDie"
20
21////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
22#pragma mark -
23////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
24
25@implementation HTTPServer
26
27/**
28 * Standard Constructor.
29 * Instantiates an HTTP server, but does not start it.
30**/
31- (id)init
32{
33 if (self = [super init])
34 {
35 // Initialize underlying asynchronous tcp/ip socket
36 asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];
37
38 // Use default connection class of HTTPConnection
39 connectionClass = [HTTPConnection self];
40
41 // Use a default port of 0
42 // This will allow the kernel to automatically pick an open port for us
43 port = 0;
44
45 // Initialize an array to hold all the HTTP connections
46 connections = [[NSMutableArray alloc] init];
47
48 // And register for notifications of closed connections
49 [[NSNotificationCenter defaultCenter] addObserver:self
50 selector:@selector(connectionDidDie:)
51 name:HTTPConnectionDidDieNotification@ "HTTPConnectionDidDie"
52 object:nil0];
53 }
54 return self;
55}
56
57/**
58 * Standard Deconstructor.
59 * Stops the server, and clients, and releases any resources connected with this instance.
60**/
61- (void)dealloc
62{
63 // Remove notification observer
64 [[NSNotificationCenter defaultCenter] removeObserver:self];
65
66 // Stop the server if it's running
67 [self stop];
68
69 // Release all instance variables
70 [documentRoot release];
71 [asyncSocket release];
72 [connections release];
73
74 [super dealloc];
75}
76
77////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
78#pragma mark Server Configuration:
79////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
80
81/**
82 * Returns the delegate connected with this instance.
83**/
84- (id)delegate
85{
86 return delegate;
87}
88
89/**
90 * Sets the delegate connected with this instance.
91**/
92- (void)setDelegate:(id)newDelegate
93{
94 delegate = newDelegate;
95}
96
97/**
98 * The document root is filesystem root for the webserver.
99 * Thus requests for /index.html will be referencing the index.html file within the document root directory.
100 * All file requests are relative to this document root.
101**/
102- (NSURL *)documentRoot {
103 return documentRoot;
104}
105- (void)setDocumentRoot:(NSURL *)value
106{
107 if (![documentRoot isEqual:value])
108 {
109 [documentRoot release];
110 documentRoot = [value copy];
111 }
112}
113
114/**
115 * The connection class is the class that will be used to handle connections.
116 * That is, when a new connection is created, an instance of this class will be intialized.
117 * The default connection class is HTTPConnection.
118 * If you use a different connection class, it is assumed that the class extends HTTPConnection
119**/
120- (Class)connectionClass {
121 return connectionClass;
122}
123- (void)setConnectionClass:(Class)value
124{
125 connectionClass = value;
126}
127
128
129/**
130 * The port to listen for connections on.
131 * By default this port is initially set to zero, which allows the kernel to pick an available port for us.
132 * After the HTTP server has started, the port being used may be obtained by this method.
133**/
134- (UInt16)port {
135 return port;
136}
137- (void)setPort:(UInt16)value {
138 port = value;
139}
140
141
142- (NSString *)localHost{
143 NSEnumerator* e = [[[NSHost currentHost] addresses] objectEnumerator];
144 NSString* addr;
145 while (addr = (NSString*)[e nextObject])
146 {
147 if ([[addr componentsSeparatedByString:@"."] count] == 4 && ![addr isEqual:@"127.0.0.1"])
148 {
149 return addr;
150 }
151 }
152 return [NSString stringWithFormat:@"127.0.0.1"]; // No non-loopback IPv4 addresses exist
153}
154
155- (void)setTransfer:(EKEzvOutgoingFileTransfer *)newTransfer{
156 if (transfer !=newTransfer)
157 {
158 [transfer release];
159 transfer = [newTransfer retain];
160 }
161}
162- (EKEzvOutgoingFileTransfer *)transfer{
163 return transfer;
164}
165
166////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
167#pragma mark Server Control:
168////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
169
170- (BOOL)start:(NSError **)error
171{
172 BOOL success = [asyncSocket acceptOnPort:port error:error];
173
174 if (success)
175 {
176 // Update our port number
177 [self setPort:[asyncSocket localPort]];
178
179 // Output console message for debugging purposes
180 // NSLog(@"Started HTTP server on port %hu", port);
181 }
182
183 return success;
184}
185
186- (BOOL)stop
187{
188 // Now stop the asynchronouse tcp server
189 // This will prevent it from accepting any more connections
190 [asyncSocket disconnect];
191
192 // Now stop all HTTP connections the server owns
193 [connections removeAllObjects];
194
195 return YES( BOOL ) 1;
196}
197
198////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
199#pragma mark Server Status:
200////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
201
202/**
203 * Returns the number of clients that are currently connected to the server.
204**/
205- (int)numberOfHTTPConnections
206{
207 return [connections count];
208}
209
210////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
211#pragma mark AsyncSocket Delegate Methods:
212////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
213
214-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
215{
216 /*The contact has attempted to download so tell the main thread that we are now longer waiting on a remote user */
217 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
218 [transfer userBeganDownload];
219
220 id newConnection = [[connectionClass alloc] initWithAsyncSocket:newSocket forServer:self];
221 [connections addObject:newConnection];
222 [newConnection release];
223}
224
225/**
226 * This method is automatically called when a notification of type HTTPConnectionDidDieNotification is posted.
227 * It allows us to remove the connection from our array.
228**/
229- (void)connectionDidDie:(NSNotification *)notification
230{
231 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
232 if (self != (HTTPServer *)[[notification object] server]) {
233 return;
234 } else {
235 if (![transfer moreFilesToDownload]) {
236 [transfer userFinishedDownload];
237 }
238 // [connections removeObject:[notification object]];
239
240 }
241}
242
243@end
244
245
246////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
247#pragma mark -
248////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
249
250
251@implementation HTTPConnection
252
253
254/**
255 * This method is automatically called (courtesy of Cocoa) before the first instantiation of this class.
256 * We use it to initialize any static variables.
257**/
258+ (void)initialize
259{
260 static BOOL initialized = NO( BOOL ) 0;
261 if (!initialized)
262 {
263
264 initialized = YES( BOOL ) 1;
265 }
266}
267
268
269/**
270 * Sole Constructor.
271 * Associates this new HTTP connection with the given AsyncSocket.
272 * This HTTP connection object will become the socket's delegate and take over responsibility for the socket.
273**/
274- (id)initWithAsyncSocket:(AsyncSocket *)newSocket forServer:(HTTPServer *)myServer
275{
276 if (self = [super init])
277 {
278 // Take over ownership of the socket
279 asyncSocket = [newSocket retain];
280 [asyncSocket setDelegate:self];
281
282 // Store reference to server
283 // Note that we do not retain the server. Parents retain their children, children do not retain their parents.
284 server = myServer;
285
286
287 // Create a new HTTP message
288 // Note the second parameter is YES, because it will be used for HTTP requests from the client
289 request = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, YES( BOOL ) 1);
290
291 // And now that we own the socket, and we have our CFHTTPMessage object (for requests) ready,
292 // we can start reading the HTTP requests...
293 [asyncSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT- 1 tag:HTTP_REQUEST15];
294 }
295 return self;
296}
297
298/**
299 * Standard Deconstructor.
300**/
301- (void)dealloc
302{
303 [asyncSocket release];
304 if (request) CFRelease(request);
305
306 [super dealloc];
307}
308
309////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
310#pragma mark Connection Control:
311////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
312/**
313 * This method is called after a full HTTP request has been received.
314 * The current request is in the CFHTTPMessage request variable.
315**/
316- (void)replyToHTTPRequest
317{
318 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
319
320 // Print out the entire httprequest
321// NSDictionary *headers = [(NSDictionary *)CFHTTPMessageCopyAllHeaderFields(request) autorelease];
322// NSString *requestMethod = [(NSString *)CFHTTPMessageCopyRequestMethod(request) autorelease];
323// NSString *requestVersion = [(NSString *)CFHTTPMessageCopyVersion(request) autorelease];
324// NSURL *requestURI = [(NSURL *)CFHTTPMessageCopyRequestURL(request) autorelease];
325
326 NSString *encoding = [(NSString *)CFHTTPMessageCopyHeaderFieldValue(request, CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Accept-Encoding" "" ) )
("Accept-Encoding")) autorelease];
327 bool_Bool isAppleSingle = NO( BOOL ) 0;
328 if ([encoding isEqualToString:@"AppleSingle"]) {
329 isAppleSingle = YES( BOOL ) 1;
330 }
331
332 NSString *connection = [(NSString *)CFHTTPMessageCopyHeaderFieldValue(request, CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Connection" "" ) )
("Connection")) autorelease];
333 bool_Bool isKeepAlive = NO( BOOL ) 0;
334 if ([connection isEqualToString:@"keep-alive"]) {
335 isKeepAlive = YES( BOOL ) 1;
336 }
337
338 // NSLog(@"HTTP Server: Version - %@\nMethod - %@\nURI - %@\nRequest - %@", requestVersion, requestMethod, requestURI, headers);
339
340 // Check the HTTP version
341 // If it's anything but HTTP version 1.1, we don't support it
342 NSString *version = [(NSString *)CFHTTPMessageCopyVersion(request) autorelease];
343 if (!version || ![version isEqualToString:(NSString *)kCFHTTPVersion1_1]) {
344 //NSLog(@"HTTP Server: Error 505 - Version Not Supported");
345
346 // Status Code 505 - Version Not Supported
347 CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 505, NULL( ( void * ) 0 ), (CFStringRef)version);
348 CFHTTPMessageSetHeaderFieldValue(response, CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Content-Length" "" ) )
("Content-Length"), CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"0" "" ) )
("0"));
349 NSData *responseData = [(NSData *)CFHTTPMessageCopySerializedMessage(response) autorelease];
350 [asyncSocket writeData:responseData withTimeout:WRITE_ERROR_TIMEOUT30 tag:HTTP_RESPONSE30];
351 CFRelease(response);
352 [[server transfer] userFailedDownload];
353 return;
354 }
355
356 // Check HTTP method
357 // If no method was passed, issue a Bad Request response
358 NSString *method = [(NSString *)CFHTTPMessageCopyRequestMethod(request) autorelease];
359 if (!method) {
360 //NSLog(@"HTTP Server: Error 400 - Bad Request");
361
362 // Status Code 400 - Bad Request
363 CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 400, NULL( ( void * ) 0 ), kCFHTTPVersion1_1);
364 CFHTTPMessageSetHeaderFieldValue(response, CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Content-Length" "" ) )
("Content-Length"), CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"0" "" ) )
("0"));
365 NSData *responseData = [(NSData *)CFHTTPMessageCopySerializedMessage(response) autorelease];
366 [asyncSocket writeData:responseData withTimeout:WRITE_ERROR_TIMEOUT30 tag:HTTP_RESPONSE30];
367 CFRelease(response);
368 [[server transfer] userFailedDownload];
369 return;
370 }
371
372 // Extract requested URI
373 NSURL *uri = [(NSURL *)CFHTTPMessageCopyRequestURL(request) autorelease];
374
375 // Respond properly to HTTP 'GET' and 'HEAD' commands
376 if ([method isEqualToString:@"GET"] || [method isEqualToString:@"HEAD"]) {
377 // AWEzvLog(@"responding to get/head");
378 NSData *data = [self dataForURI:[uri relativeString] appleSingle:isAppleSingle];
379
380 if (!data)
381 {
382 /*We have a request for a non-existant file so we need to stop the transfer */
383 [[server transfer] userFailedDownload];
384 return;
385 }
386
387 // Status Code 200 - OK
388 CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL( ( void * ) 0 ), kCFHTTPVersion1_1);
389 [self setHeaderFields:response forURI:[uri relativeString] appleSingle: isAppleSingle keepAlive:isKeepAlive];
390 NSString *contentLength = [NSString stringWithFormat:@"%i", [data length]];
391 CFHTTPMessageSetHeaderFieldValue(response, CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Content-Length" "" ) )
("Content-Length"), (CFStringRef)contentLength);
392
393 //NSDictionary *responseHeaders = [(NSDictionary *)CFHTTPMessageCopyAllHeaderFields(response) autorelease];
394 // NSLog(@"Sending Headers - %@", responseHeaders);
395
396
397 // If they issue a 'HEAD' command, we don't have to include the file
398 // If they issue a 'GET' command, we need to include the file
399 if ([method isEqual:@"HEAD"])
400 {
401 NSData *responseData = [(NSData *)CFHTTPMessageCopySerializedMessage(response) autorelease];
402 [asyncSocket writeData:responseData withTimeout:WRITE_HEAD_TIMEOUT30 tag:HTTP_RESPONSE30];
403 } else {
404 // Previously, we would use the CFHTTPMessageSetBody method here.
405 // This caused problems, however, if the data was large.
406 // For example, if the data represented a 500 MB movie on the disk, this method would thrash the OS!
407
408 NSData *responseData = [(NSData *)CFHTTPMessageCopySerializedMessage(response) autorelease];
409 [asyncSocket writeData:responseData withTimeout:WRITE_HEAD_TIMEOUT30 tag:HTTP_PARTIAL_RESPONSE29];
410 [asyncSocket writeData:data withTimeout:WRITE_BODY_TIMEOUT- 1 tag:HTTP_RESPONSE30];
411 }
412
413 CFRelease(response);
414 return;
415 }
416
417 //NSLog(@"HTTP Server: Error 405 - Method Not Allowed: %@", method);
418
419 // Status code 405 - Method Not Allowed
420 CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 405, NULL( ( void * ) 0 ), kCFHTTPVersion1_1);
421 NSData *responseData = [(NSData *)CFHTTPMessageCopySerializedMessage(response) autorelease];
422 [asyncSocket writeData:responseData withTimeout:WRITE_ERROR_TIMEOUT30 tag:HTTP_RESPONSE30];
423 CFRelease(response);
424
425 [[server transfer] userFailedDownload];
426
427 return;
428}
429
430/**
431 * This method transforms the relative URL to the full URL.
432 * It takes care of requests such as "/", transforming them to "/index.html".
433 * This method can easily be overriden to perform more advanced lookups.
434**/
435- (NSData *)dataForURI:(NSString *)path appleSingle:(BOOL)isAppleSingle
436{
437 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
438 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
439
440 NSMutableData *data = [NSMutableData data];
441
442
[1] Taking false branch.
443 if ([[server transfer] isDirectory] && [[server transfer] isBaseURIForDirectoryTransfer: path]) {
444 /*If this is the base url for a directory transfer then we need to send the xml */
445 [data appendData:[[server transfer] directoryXMLData]];
446 // AWEzvLog(@"%@", data);
447 } else {
448
449 /*Check to see whether we should get applesingle data*/
[2] Taking false branch.
450 if (isAppleSingle) {
451 [data appendData:[[server transfer] appleSingleDataForURI:path]];
452 }
453
454 /*Send the AppleSingle data along with the other stuff!*/
455 /*We will get the path so let's load the data from the path */
[3] Taking true branch.
456 if (data) {
457 NSError *error = nil0;
[4] Method returns an object with a +1 retain count (owning reference).
458 NSData *fileData = [[NSData alloc] initWithContentsOfFile:[[server transfer] fileDataForURI:path] options:(NSMappedRead | NSUncachedRead) error:&error];
[5] Taking true branch.
459 if (error || (fileData == nil0)) {
[6] Object allocated on line 458 and stored into 'fileData' is no longer referenced after this point and has a retain count of +1 (object leaked).
460 [data autorelease];
461 data = nil0;
462 } else {
463 [data appendData: fileData];
464 [fileData release];
465 }
466 }
467 }
468 return data;
469}
470
471- (void) setHeaderFields:(CFHTTPMessageRef)messageRef forURI:(NSString *)path appleSingle:(BOOL)isAppleSingle keepAlive:(BOOL)isKeepAlive
472{
473 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
474 /*Set the iChat header field responses*/
475 CFHTTPMessageRef response = messageRef;
476 CFHTTPMessageSetHeaderFieldValue(response,CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Accept-Ranges" "" ) )
("Accept-Ranges"),CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"bytes" "" ) )
("bytes"));
477 CFHTTPMessageSetHeaderFieldValue(response,CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Connection" "" ) )
("Connection"),CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"close" "" ) )
("close"));
478 if (isAppleSingle)
479 CFHTTPMessageSetHeaderFieldValue(response,CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Content-Encoding" "" ) )
("Content-Encoding"),CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"AppleSingle" "" ) )
("AppleSingle"));
480 CFHTTPMessageSetHeaderFieldValue(response,CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Date" "" ) )
("Date"),(CFStringRef)[[NSDate date] description]);
481 CFHTTPMessageSetHeaderFieldValue(response,CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Server" "" ) )
("Server"),CFSTR( ( CFStringRef ) __builtin___CFStringMakeConstantString ( ""
"Fez (Mac OS X)" "" ) )
("Fez (Mac OS X)"));
482}
483////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
484#pragma mark AsyncSocket Delegate Methods:
485////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
486
487/**
488 * This method is called immediately prior to opening up the stream.
489 * This is the time to manually configure the stream if necessary.
490**/
491- (BOOL)onSocketWillConnect:(AsyncSocket *)sock
492{
493 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
494
495 return YES( BOOL ) 1;
496}
497
498/**
499 * This method is called after the socket has successfully read data from the stream.
500 * Remember that this method will only be called after the socket reaches a CRLF, or after it's read the proper length.
501**/
502- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag
503{
504 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
505
506 // Append the header line to the http message
507 CFHTTPMessageAppendBytes(request, [data bytes], [data length]);
508 if (!CFHTTPMessageIsHeaderComplete(request)) {
509 // We don't have a complete header yet
510 // That is, we haven't yet received a CRLF on a line by itself, indicating the end of the header
511 [asyncSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT- 1 tag:HTTP_REQUEST15];
512 }
513 else
514 {
515 // We have an entire HTTP request from the client
516 // Now we need to reply to it
517 [self replyToHTTPRequest];
518 }
519}
520
521/**
522 * This method is called after the socket has successfully written data to the stream.
523 * Remember that this method will be called after a complete response to a request has been written.
524**/
525- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
526{
527 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
528
529 // There are two possible tags: HTTP_RESPONSE and HTTP_PARTIAL_RESPONSE
530 // A partial response represents a header response with a body following it,
531 // so we still need to wait til the body is sent too.
532
533 if (tag == HTTP_RESPONSE30)
534 {
535 // Release the old request, and create a new one
536 if (request) CFRelease(request);
537 request = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, YES( BOOL ) 1);
538
539 // And start listening for more requests
540 [asyncSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT- 1 tag:HTTP_REQUEST15];
541 }
542}
543
544/**
545 * This message is sent:
546 * - if there is an connection, time out, or other i/o error.
547 * - if the remote socket cleanly disconnects.
548 * - before the local socket is disconnected.
549**/
550- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
551{
552 if (err)
553 {
554 }
555}
556
557/**
558 * Sent after the socket has been disconnected.
559**/
560- (void)onSocketDidDisconnect:(AsyncSocket *)sock
561{
562 // AWEzvLog(@"%s", __PRETTY_FUNCTION__);
563
564 // Post notification of dead connection
565 // This will allow our server to release it from it's array of connections
566 [[NSNotificationCenter defaultCenter] postNotificationName:HTTPConnectionDidDieNotification@ "HTTPConnectionDidDie" object:self];
567}
568
569
570- (void)didSendDataWithLength:(UInt32)length
571{
572 [[server transfer] didSendDataWithLength:length];
573}
574
575- (HTTPServer *)server
576{
577 return server;
578}
579@end