main
sandyx 1 year ago
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…
Cancel
Save