How to Write a Cocoa Web Server
Subject:   Two big problems with this code
Date:   2009-12-10 15:37:18
From:   bhearn
I don't know if anyone is still looking at this, but in case somebody runs across it and wants to use it as a starting point, there are some problems you should be aware of.

First, the article says "One of the problems in writing server software is the need to make sure that the server is always able to accept new requests no matter how busy it is fulfilling the old ones. ... But fortunately, Cocoa provides us with a class that relieves us of creating a multi-threaded application: NSFileHandle"

Yes and no. NSFileHandle doesn't do quite what the author thinks it does. As a result, this server code is not in fact able to accept new requests no matter how busy it is; it's effectively single threaded.

The problem is that the NSFileHandle methods acceptConnectionInBackgroundAndNotify and readInBackgroundAndNotify, while they do wait for connections / data on a background thread, in fact send their notifications to the *main* thread; they are delivered in the main run loop. So, if the server is busy processing a request, it won't actually accept a new request (or a new connection) until it's done with the first request. Not that there's really anything wrong with that for a basic, instructional server. But it doesn't do what is claimed. Furthermore, it makes a lot of the code useless. For example, there is an NSArray of current requests, but there can ever only be one request at a time. So basically all of the code to manage requests is irrelevant. (Also if there is a reason to represent connections with dictionaries, it escapes me.)

Admittedly the documentation for NSFileHandle doesn't make this behavior especially clear, but that is what it says, and that is what it does, as can be easily verified by breaking in a notification handler in the debugger. You can see that it's being called on the main thread's run loop.

The second problem is how the code handles closing connections. A connection decides to close itself when either it gets a bad request, or it gets a zero-byte read (indicating the client has closed the socket). The problem is, the SimpleHTTPConnection object calls its delegate (the SimpleHTTPServer) to close the connection; the server then removes the connection from its table of connections, which removes its last reference count. So the SimpleHTTPConnection object is deallocated -- but while it is still running!

This doesn't actually crash the program, but only because SimpleHTTPConnection:dataReceivedNotification: immediately returns after closing the connection. At best, this is very bad style; if there were any code added here; it would crash.