Search
Recommended for You

Creating an iPhone-based Web Service: Part 3


Over the last two weeks, I've shown you how to create a socket to listen for web requests and how to produce an artificial index.html file that advertises the files you're willing to serve from your iPhone. Today, I'll wrap things up by adding error pages and the actual file server.

Error Pages

Error pages are an important part of web service, no matter how trivial the server. They allow you to communicate application state to a client so the user knows why a request was not fulfilled.

To produce errors, output a well-formed header followed by the actual error HTML text. Here's how to produce the header by serving text/html content:

	NSString *outcontent = [NSString stringWithFormat:@"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"];
	write (fd, [outcontent UTF8String], [outcontent length]);

Follow this by doing a write of the actual HTML-based error message and then close(fd) to finish serving the page.

Serving Files

To serve files, supply a proper mime type in the header. What I have ended up doing for this is creating a MIMEHelper class, whose entire job is to match file extensions to mime type. It's nothing but a series of compare statements, like:

@implementation MIMEHelper
+ (NSString *) mimeForExt: (NSString *) ext
{
	NSString *uc = [ext uppercaseString];
    if([uc isEqualToString:[@"3dm" uppercaseString]]) return @"x-world/x-3dmf";
    if([uc isEqualToString:[@"3dmf" uppercaseString]]) return @"x-world/x-3dmf";
    if([uc isEqualToString:[@"a" uppercaseString]]) return @"application/octet-stream";
    ...and so forth...

This isn't especially efficient but it runs fast enough to satisfy web service without significant delays. My MIMEHelper scans through all the extensions I could google up, and compares the requested file name to those.

You can easily find lists of file extensions and mime types around the net. The best place to look are the various standards organizations. As you can see from this snippet, it's easy to create your own mime type helper class. This one has one class method, mimeForExt:, which is called with the parameter from [filename pathExtension].

To send data, follow the header with a pair of carriage-return/new-line characters and feed the data byte-for-byte over the connection. The following code does exactly that, producing the header and feeding the data, then closing the file stream.

	NSString *mime = [MIMEHelper mimeForExt:[filereq pathExtension]];
	if (!mime)
	{
		printf("Error recovering mime type.\n");
		[self produceError:@"Sorry. This file type is not supported." forFD:fd];
		return;
	}
	
	// Output the file
	NSString *outcontent = [NSString stringWithFormat:@"HTTP/1.0 200 OK\r\nContent-Type: %@\r\n\r\n", mime];
	write (fd, [outcontent UTF8String], [outcontent length]);
	NSData *data = [NSData dataWithContentsOfFile:filereq];
	if (!data)
	{
		printf("Error: file not found.\n");
		[self produceError:@"File was not found. Please check the requested path and try again." forFD:fd];
		return;
	}
	printf("Writing %d bytes from file\n", [data length]);
	write(fd, [data bytes], [data length]);
	close(fd);
	
	[pool release];

You can make your file server as complicated as you need or as simple as this. It's up to you to determine how you want to interpret each GET request. You can limit those requests to search through a given folder, such as the DCIM onboard camera images or you can use a current working directory and search for requests that end in @"/" to allow your users to navigate up and down the iPhone directory tree.

The best thing about building a simple web server like this is that you can use any web browser on any computer without needing a special client. This not only lets you share files from your iPhone but makes it compatible with virtually any computer.

Got questions? Have ideas? Drop a note in the comments.

AddThis Social Bookmark Button
Comments (6)

6 Comments

Titus said:

Awesome work man, thanks so much!

Hi Erica,

I would suggest to use an existent file with mime types, like the one from the standard apache distribution. You can find it on every Mac OS X installation under /etc/apache2/mime.types

It's really easy to parse the file format and it's more efficient than your solution. You'll have less work, too.

Rob said:

As another alternative, sticking them in a dictionary is pretty:

NSDictionary *mimeTypes = [NSDictionary dictionaryWithObjectsAndKeys:
	@"image/png", @"png",
	@"image/vnd.microsoft.icon", @"ico",
nil];

NSString *mimeType = [mimeTypes objectForKey: …];

Cache the dictionary someone and lookups should theoretically be O(1) on average.

I’m using this for the HTTP server for the next release of Litho Graph.

Phil said:

Hi Erica,

Congrats on the book, it's an excellent reference. I'm currently testing some web serving code in my own iPhone app and I'm finding problems trying to serve large files i.e. 100MBs and more. Is there a technique that could be used to serve such files by splitting them in chunks? Does HTML support that?

Thanks in advance for any pointers you can give me,

Phil

iByung said:

How do I access the web server from other phone? what would be the URL or IP to access the iphone web server?

thanks,
B.

john Gladman said:

Hi Erica

I want to be able to input a file name on the iphone then
start to read/write to that file which will reside on a server.

Do you have any example code on how to do this??

Thanks

John

Leave a comment