Http Server in Objective-C

Here’s the translation of the HTTP server example from Go to Objective-C:

Our first HTTP server example demonstrates how to create a basic server using the Foundation framework.

#import <Foundation/Foundation.h>

@interface MyHTTPServer : NSObject <NSNetServiceDelegate>
@property (nonatomic, strong) NSNetService *netService;
@property (nonatomic, strong) NSFileHandle *listeningSocket;
@end

@implementation MyHTTPServer

- (void)startServer {
    // Create a socket
    CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL);
    if (!socket) {
        NSLog(@"Error creating socket");
        return;
    }

    // Set socket options
    int yes = 1;
    setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));

    // Set up the IPv4 endpoint
    struct sockaddr_in addr4;
    memset(&addr4, 0, sizeof(addr4));
    addr4.sin_len = sizeof(addr4);
    addr4.sin_family = AF_INET;
    addr4.sin_port = htons(8090);
    addr4.sin_addr.s_addr = htonl(INADDR_ANY);

    // Bind the socket
    CFDataRef addressData = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4, sizeof(addr4));
    if (CFSocketSetAddress(socket, addressData) != kCFSocketSuccess) {
        NSLog(@"Error binding to address");
        CFRelease(addressData);
        CFRelease(socket);
        return;
    }
    CFRelease(addressData);

    // Set up the listening file handle
    CFRunLoopSourceRef socketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), socketSource, kCFRunLoopDefaultMode);
    CFRelease(socketSource);

    self.listeningSocket = [[NSFileHandle alloc] initWithFileDescriptor:CFSocketGetNative(socket) closeOnDealloc:YES];

    // Start listening for connections
    [self.listeningSocket acceptConnectionInBackgroundAndNotify];

    // Set up notifications
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNewConnection:) name:NSFileHandleConnectionAcceptedNotification object:nil];

    NSLog(@"Server started on port 8090");
}

- (void)handleNewConnection:(NSNotification *)notification {
    NSDictionary *userInfo = notification.userInfo;
    NSFileHandle *fileHandle = userInfo[NSFileHandleNotificationFileHandleItem];

    // Handle the incoming request
    NSData *data = [fileHandle readDataToEndOfFile];
    NSString *request = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    // Process the request and generate a response
    NSString *response;
    if ([request containsString:@"GET /hello"]) {
        response = @"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhello\n";
    } else if ([request containsString:@"GET /headers"]) {
        response = [self generateHeadersResponse:request];
    } else {
        response = @"HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\n\r\nNot Found\n";
    }

    // Send the response
    [fileHandle writeData:[response dataUsingEncoding:NSUTF8StringEncoding]];
    [fileHandle closeFile];

    // Continue listening for new connections
    [self.listeningSocket acceptConnectionInBackgroundAndNotify];
}

- (NSString *)generateHeadersResponse:(NSString *)request {
    NSMutableString *responseBody = [NSMutableString string];
    NSArray *lines = [request componentsSeparatedByString:@"\r\n"];
    for (NSString *line in lines) {
        if ([line containsString:@": "]) {
            [responseBody appendFormat:@"%@\n", line];
        }
    }
    
    NSString *response = [NSString stringWithFormat:@"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n%@", responseBody];
    return response;
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyHTTPServer *server = [[MyHTTPServer alloc] init];
        [server startServer];
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

This Objective-C implementation creates a basic HTTP server that listens on port 8090. It includes two routes: /hello and /headers.

The startServer method sets up a socket and starts listening for incoming connections. When a new connection is accepted, the handleNewConnection: method is called to process the request and send a response.

The /hello route simply responds with “hello”, while the /headers route echoes back the headers from the incoming request.

To run the server:

  1. Save the code in a file named HTTPServer.m.
  2. Compile the code:
$ clang -framework Foundation HTTPServer.m -o HTTPServer
  1. Run the compiled binary:
$ ./HTTPServer

The server will start and listen on port 8090. You can then use curl or a web browser to access the routes:

$ curl localhost:8090/hello
hello

$ curl localhost:8090/headers

This example demonstrates basic HTTP server functionality in Objective-C, although it’s worth noting that for more complex server applications, you might want to consider using higher-level frameworks or libraries designed specifically for web servers in iOS/macOS development.