it works
commit
854fb6548b
@ -0,0 +1,15 @@
|
||||
#ifndef HTML_H
|
||||
#define HTML_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//cheap html wrapper
|
||||
@interface NSString (HTML)
|
||||
|
||||
-(id) wrapWithTag: (NSString *) tag;
|
||||
-(id) wrapWithTag: (NSString *) tag href: (NSString *) ref;
|
||||
-(id) appendNewline;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,29 @@
|
||||
#import "html.h"
|
||||
|
||||
//should maybe free the buffers
|
||||
|
||||
@implementation NSString (HTML)
|
||||
-(id) wrapWithTag: (NSString *) tag {
|
||||
int len = [self length] + [tag length] + [@"<" length] + [@"</" length] + ([@">" length] * 2);
|
||||
char *buffer = calloc(len, sizeof(char) + 1);
|
||||
char *tag_utf8 = [tag UTF8String];
|
||||
sprintf(buffer, "<%s>%s</%s>", tag_utf8, [self UTF8String], tag_utf8);
|
||||
NSString *ret = [NSString stringWithUTF8String: buffer];
|
||||
//free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
-(id) wrapWithTag: (NSString *) tag href: (NSString *) ref {
|
||||
int len = [self length] + [tag length] + [ref length] + [@"<" length] + [@"</" length] + ([@">" length] * 2) + 1;
|
||||
char *buffer = calloc(len, sizeof(char) + 1);
|
||||
char *tag_utf8 = [tag UTF8String];
|
||||
sprintf(buffer, "<%s href=\"%s\">%s</%s>", tag_utf8, [ref UTF8String], [self UTF8String], tag_utf8);
|
||||
NSString *ret = [NSString stringWithUTF8String: buffer];
|
||||
//free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
-(id) appendNewline {
|
||||
return [self stringByAppendingString: @"\r\n"];
|
||||
}
|
||||
@end
|
||||
@ -0,0 +1,43 @@
|
||||
#ifndef HTTP_H
|
||||
#define HTTP_H
|
||||
|
||||
#import "socket.h"
|
||||
#import "lambda.h"
|
||||
|
||||
static NSString *httpHeader = @"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
|
||||
static NSString *httpMultipart = @"HTTP/1.1 200 OK\r\nContent-Type: multipart/form-data\r\n\r\n";
|
||||
|
||||
enum RequestType {
|
||||
GET = 0,
|
||||
POST = 1,
|
||||
};
|
||||
|
||||
@interface HTTPRequest : NSObject {
|
||||
NSString *type;
|
||||
NSString *uri;
|
||||
NSString *host;
|
||||
}
|
||||
@property (assign,nonatomic) NSString *type;
|
||||
@property (assign,nonatomic) NSString *uri;
|
||||
@property (assign,nonatomic) NSString *host;
|
||||
+(id) parseRequest: (NSString *) req;
|
||||
@end
|
||||
|
||||
@interface HTTPServer : NSObject {
|
||||
NSSocket *sock;
|
||||
NSMutableDictionary *handlers;
|
||||
bool open;
|
||||
}
|
||||
|
||||
+(id) httpServerWithPort: (int) port;
|
||||
-(id) read;
|
||||
-(BOOL) listen;
|
||||
-(BOOL) write: (NSString *) data;
|
||||
-(BOOL) writeData: (NSData *) data;
|
||||
-(void) close;
|
||||
-(void) assignHandler: (NSLambda *) handler to: (NSString *) url;
|
||||
-(void) handleRequest: (HTTPRequest *) req;
|
||||
-(void) handleURI: (NSString *) URI;
|
||||
@end
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,99 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "http.h"
|
||||
|
||||
//cheap parsing
|
||||
@implementation HTTPRequest
|
||||
@synthesize type;
|
||||
@synthesize uri;
|
||||
@synthesize host;
|
||||
|
||||
//cheap parsing
|
||||
+(id) parseRequest: (NSString *) req {
|
||||
HTTPRequest *request = [[HTTPRequest alloc] init];
|
||||
[request autorelease];
|
||||
NSArray *arr = [req componentsSeparatedByString: @"\n"];
|
||||
NSArray *arr2 = [[arr objectAtIndex: 0] componentsSeparatedByString: @" "];
|
||||
request->type = [arr objectAtIndex: 0];
|
||||
if ([arr count] > 1)
|
||||
request->host = [arr objectAtIndex: 1];
|
||||
if ([arr2 count] > 1)
|
||||
request->uri = [arr2 objectAtIndex: 1];
|
||||
return request;
|
||||
}
|
||||
@end
|
||||
|
||||
id default_handler(id server ,id obj) {
|
||||
[server write: @"idk how to handle that"];
|
||||
return server;
|
||||
}
|
||||
|
||||
@implementation HTTPServer
|
||||
+(id) httpServerWithPort: (int) port {
|
||||
HTTPServer *server = [[super alloc] init];
|
||||
server->sock = [NSSocket socketWithPort: port];
|
||||
server->handlers = [NSMutableDictionary dictionary];
|
||||
server->open = false;
|
||||
|
||||
[server assignHandler: [NSLambda lambdaWithReducer: default_handler] to: @"DEFAULT"];
|
||||
return server;
|
||||
}
|
||||
|
||||
-(id) read {
|
||||
return [sock read];
|
||||
}
|
||||
|
||||
-(BOOL) listen {
|
||||
return [sock listen];
|
||||
}
|
||||
|
||||
-(BOOL) write: (NSString *) data {
|
||||
NSString *resp = data;
|
||||
|
||||
if (!open) {
|
||||
open = true;
|
||||
resp = [httpHeader stringByAppendingString: data];
|
||||
}
|
||||
|
||||
[sock write: resp];
|
||||
}
|
||||
|
||||
-(BOOL) writeData: (NSData *) data {
|
||||
if (!open) {
|
||||
open = true;
|
||||
[sock write: httpMultipart];
|
||||
}
|
||||
|
||||
[sock writeData: data];
|
||||
}
|
||||
|
||||
-(void) close {
|
||||
open = false;
|
||||
[sock close];
|
||||
}
|
||||
|
||||
-(void) assignHandler: (NSLambda *) handler to: (NSString *) url {
|
||||
[handlers setObject: handler forKey: url];
|
||||
}
|
||||
|
||||
-(void) handleRequest: (HTTPRequest *) req {
|
||||
id handler = [handlers objectForKey: [req uri]];
|
||||
|
||||
if (handler != nil) {
|
||||
id nothing = [handler reduce: self with: [req uri]];
|
||||
return;
|
||||
}
|
||||
|
||||
id nothing = [[handlers objectForKey: @"DEFAULT"] call];
|
||||
}
|
||||
|
||||
-(void) handleURI: (NSString *) URI {
|
||||
id handler = [handlers objectForKey: URI];
|
||||
|
||||
if (handler != nil) {
|
||||
id nothing = [handler reduce: self with: URI];
|
||||
return;
|
||||
}
|
||||
|
||||
id nothing = [[handlers objectForKey: @"DEFAULT"] call];
|
||||
}
|
||||
@end
|
||||
@ -0,0 +1,35 @@
|
||||
#ifndef LAMBDA_H
|
||||
#define LAMBDA_H
|
||||
|
||||
typedef id (*lambda)(id);
|
||||
typedef id (*sequence[])(id);
|
||||
typedef id (*reducer)(id , id);
|
||||
|
||||
typedef union Lambda {
|
||||
id (*lambdaNoArgs)(void);
|
||||
id (*lambda)(id);
|
||||
id (*reduce)(id, id);
|
||||
} Anon;
|
||||
|
||||
//experimental
|
||||
@interface NSLambda : NSObject {
|
||||
Anon anon;
|
||||
//reducer lambdaFunction;
|
||||
}
|
||||
@property Anon anon;
|
||||
|
||||
+(id) lambdaWithLambda: (lambda) lambda;
|
||||
+(id) lambdaWithReducer: (reducer) reducer;
|
||||
-(id) call;
|
||||
-(id) reduce: (id) acc with: (id) obj;
|
||||
@end
|
||||
|
||||
@interface NSMutableArray (Lambda)
|
||||
-(NSMutableArray *) map: (lambda) fn;
|
||||
-(NSMutableArray *) filter: (lambda) fn;
|
||||
-(NSMutableArray *) reduce: (reducer) fn withAccumulator: (id) acc;
|
||||
-(NSMutableArray *) mapSequence: (sequence) fnArr count: (int) n;
|
||||
-(id) clean;
|
||||
@end
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,88 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "lambda.h"
|
||||
|
||||
@implementation NSLambda
|
||||
@synthesize anon;
|
||||
|
||||
+(id) lambdaWithLambda: (lambda) lambda {
|
||||
NSLambda *l = [NSLambda new];
|
||||
[l autorelease];
|
||||
[l setAnon: (Anon)lambda];
|
||||
return l;
|
||||
}
|
||||
|
||||
+(id) lambdaWithReducer: (reducer) reducer {
|
||||
NSLambda *l = [NSLambda new];
|
||||
[l autorelease];
|
||||
[l setAnon: (Anon)reducer];
|
||||
return l;
|
||||
}
|
||||
|
||||
-(id) call {
|
||||
return anon.lambda(@"test");
|
||||
}
|
||||
|
||||
-(id) reduce: (id) acc with: (id) obj {
|
||||
return anon.reduce(acc, obj);
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSMutableArray (Lambda)
|
||||
|
||||
//map function to array, return array of same length
|
||||
-(NSMutableArray *) map: (lambda) fn {
|
||||
NSMutableArray *r = [[NSMutableArray alloc] init];
|
||||
[r autorelease];
|
||||
|
||||
for (id object in self) {
|
||||
[r addObject: fn(object)];
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//filter an array
|
||||
-(NSMutableArray *) filter: (lambda) fn {
|
||||
NSMutableArray *r = [[NSMutableArray alloc] init];
|
||||
[r autorelease];
|
||||
|
||||
for (id object in self) {
|
||||
id new = fn(object);
|
||||
|
||||
if ([new class] != [NSNull class]) {
|
||||
[r addObject: new];
|
||||
} else {
|
||||
[new release];
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
-(NSMutableArray *) reduce: (reducer) fn withAccumulator: (id) acc {
|
||||
for (id object in self) {
|
||||
acc = fn(acc, object);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
-(NSMutableArray *) mapSequence: (sequence) fnArr count: (int) n {
|
||||
id r = self;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
r = [r map: *fnArr[i]];
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//clean the nulls from the array
|
||||
-(id) clean {
|
||||
for (int i = 0; i < [self count]; i++) {
|
||||
[[self objectAtIndex: i] class] == [NSNull class] ? [self removeObjectAtIndex: i] : @"yes";
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
@ -0,0 +1,142 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <stdio.h>
|
||||
#import <stdlib.h>
|
||||
#import <stdbool.h>
|
||||
#import "socket.h"
|
||||
#import "lambda.h"
|
||||
#import "http.h"
|
||||
#import "html.h"
|
||||
#define PORT 8080
|
||||
|
||||
id litag(id obj) {
|
||||
return [obj wrapWithTag: @"li"];
|
||||
}
|
||||
|
||||
id atag(id obj) {
|
||||
return [obj wrapWithTag: @"a" href: obj];
|
||||
}
|
||||
|
||||
id nl(id obj) {
|
||||
return [obj appendNewline];
|
||||
}
|
||||
|
||||
id localize(id obj) {
|
||||
return [@"." stringByAppendingString: obj];
|
||||
}
|
||||
|
||||
id slash(id obj) {
|
||||
return [@"/" stringByAppendingString: obj];
|
||||
}
|
||||
|
||||
id strip_slash(id obj) {
|
||||
return [obj stringByReplacingOccurrencesOfString: @"/" withString: @""];
|
||||
}
|
||||
|
||||
id not_exist(HTTPServer *server, id obj) {
|
||||
[server write: @"404 file not found"];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id serve_file(HTTPServer *server, id obj) {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
BOOL isDir;
|
||||
BOOL fileExists = [fm fileExistsAtPath: localize(obj) isDirectory: &isDir];
|
||||
|
||||
if (fileExists == NO) {
|
||||
NSLog(@"%@ does not exist.", obj);
|
||||
[server write: @"file does not exist"];
|
||||
return server;
|
||||
}
|
||||
|
||||
NSLog(@"sending data with: %@", localize(obj));
|
||||
NSData *data = [NSData dataWithContentsOfFile: localize(obj)];
|
||||
[server writeData: data];
|
||||
return server;
|
||||
}
|
||||
|
||||
id serve_directory(HTTPServer *server, NSString *dir) {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSString *path = localize(dir);
|
||||
NSArray *contents = [fm directoryContentsAtPath: path];
|
||||
NSMutableArray *mutContents = [NSMutableArray arrayWithArray: contents];
|
||||
[mutContents autorelease];
|
||||
NSMutableArray *newContents = [[NSMutableArray alloc] init];
|
||||
[newContents autorelease];
|
||||
|
||||
for (id thing in mutContents) {
|
||||
if ([dir isEqualToString: @"/"])
|
||||
[newContents addObject: slash(thing)];
|
||||
else
|
||||
[newContents addObject: [dir stringByAppendingString: slash(thing)]];
|
||||
}
|
||||
|
||||
sequence seq = {atag, litag};
|
||||
id list = [[newContents mapSequence: seq count: 2] componentsJoinedByString: @"\r\n"];
|
||||
NSLog(@"sending directory: %@", dir);
|
||||
[server write: [list wrapWithTag: @"ul"]];
|
||||
return server;
|
||||
}
|
||||
|
||||
void initialize_subdir(HTTPServer *server, NSString *root){
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
[fm autorelease];
|
||||
NSArray *contents = [fm directoryContentsAtPath: strip_slash(root)];
|
||||
|
||||
for (id file in contents) {
|
||||
BOOL isDir;
|
||||
BOOL exists = [fm fileExistsAtPath: file isDirectory: &isDir];
|
||||
|
||||
NSString *slashFile = [root stringByAppendingString: slash(file)];
|
||||
|
||||
if (isDir == YES) {
|
||||
initialize_subdir(server, slashFile);
|
||||
[server assignHandler: [NSLambda lambdaWithReducer: serve_directory] to: slashFile];
|
||||
} else {
|
||||
[server assignHandler: [NSLambda lambdaWithReducer: serve_file] to: slashFile];
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void initialize_root(HTTPServer *server, NSString *root) {
|
||||
[server assignHandler: [NSLambda lambdaWithReducer: serve_directory] to: @"/"];
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
[fm autorelease];
|
||||
|
||||
NSString *path = [fm currentDirectoryPath];
|
||||
NSArray *contents = [fm directoryContentsAtPath: path];
|
||||
|
||||
for (id file in contents) {
|
||||
BOOL isDir;
|
||||
BOOL exists = [fm fileExistsAtPath: file isDirectory: &isDir];
|
||||
|
||||
NSString *slashFile = slash(file);
|
||||
|
||||
if (isDir == YES) {
|
||||
initialize_subdir(server, slashFile);
|
||||
[server assignHandler: [NSLambda lambdaWithReducer: serve_directory] to: slashFile];
|
||||
} else {
|
||||
[server assignHandler: [NSLambda lambdaWithReducer: serve_file] to: slashFile];
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
|
||||
HTTPServer *server = [HTTPServer httpServerWithPort: PORT];
|
||||
initialize_root(server, @"/");
|
||||
|
||||
while ([server listen]) {
|
||||
id resp = [server read];
|
||||
HTTPRequest *req = [HTTPRequest parseRequest: resp];
|
||||
[server handleRequest: req];
|
||||
[server close];
|
||||
}
|
||||
|
||||
[pool drain];
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
#ifndef SOCKET_LAMBDA_H
|
||||
#define SOCKET_LAMBDA_H
|
||||
|
||||
@interface NSSocket (Lambda)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,24 @@
|
||||
#ifndef SOCKET_H
|
||||
#define SOCKET_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
//simple socket, not very configurable yet
|
||||
@interface NSSocket : NSObject {
|
||||
int descriptor, nsocket;
|
||||
int port;
|
||||
int err;
|
||||
struct sockaddr_in address;
|
||||
char buffer[4096];
|
||||
}
|
||||
-(id) initWithPort: (int) port;
|
||||
+(id) socketWithPort: (int) port;
|
||||
-(BOOL) listen;
|
||||
-(id) read;
|
||||
-(BOOL) write: (NSString *) data;
|
||||
-(BOOL) writeData: (NSData *) data;
|
||||
-(void) close;
|
||||
@end
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,98 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "socket.h"
|
||||
|
||||
@implementation NSSocket
|
||||
-(id) initWithPort: (int) p {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
descriptor = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (descriptor < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int opt = 1;
|
||||
|
||||
err = setsockopt(descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
|
||||
if (err != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(port);
|
||||
|
||||
self->port = p;
|
||||
|
||||
err = bind(descriptor, (struct sockaddr *)&address, sizeof(address));
|
||||
if (err < 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+(id) socketWithPort: (int) port {
|
||||
NSSocket *sock = [[super alloc] init];
|
||||
[sock autorelease];
|
||||
sock->descriptor = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (sock->descriptor < 0) {
|
||||
return [NSNull new];
|
||||
}
|
||||
|
||||
int opt = 1;
|
||||
|
||||
int err = setsockopt(sock->descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
|
||||
if (err != 0) {
|
||||
return [NSNull new];
|
||||
}
|
||||
|
||||
sock->address.sin_family = AF_INET;
|
||||
sock->address.sin_addr.s_addr = INADDR_ANY;
|
||||
sock->address.sin_port = htons(port);
|
||||
|
||||
sock->port = 8080;
|
||||
|
||||
err = bind(sock->descriptor, (struct sockaddr *)&sock->address, sizeof(sock->address));
|
||||
if (err < 0) {
|
||||
return [NSNull new];
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
-(BOOL) listen {
|
||||
err = listen(descriptor, 3);
|
||||
if (err < 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
socklen_t addrlen = sizeof(address);
|
||||
nsocket = accept(descriptor, (struct sockaddr *)&address, &addrlen);
|
||||
if (socket < 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(id) read {
|
||||
read(nsocket, buffer, 1024 - 1);
|
||||
return [NSString stringWithUTF8String: buffer];
|
||||
}
|
||||
|
||||
-(BOOL) write: (NSString *) data {
|
||||
send(nsocket, [data UTF8String], [data length], 0);
|
||||
}
|
||||
|
||||
-(BOOL) writeData: (NSData *) data {
|
||||
send(nsocket, [data bytes], [data length], 0);
|
||||
}
|
||||
|
||||
//the thing returns a new socket so you gotta close it
|
||||
-(void) close {
|
||||
close(nsocket);
|
||||
}
|
||||
@end
|
||||
Loading…
Reference in New Issue