Giter Site home page Giter Site logo

crux's Introduction

CoRoUtine eXchange

This code was born out of an idea for a programming language I have been working on. The idea was to have a standard library that could be usable from C. The language has coroutines, so I wanted a way to have those usable in foreign code. I'm still not sure if that's a good idea or not, but I figure some of this code could at least be interesting. This is the task-based concurrency system. I have yet to get around to channels and a multi-event selection system.

The coroutines are currently only supported on x86 processors (32-bit and 64-bit), and they have been tested on Linux, FreeBSD, and Mac OS X. The library as a whole has been developed against Linux and Mac OS. It probably needs a little to work to compile on BSD, but I haven't gotten around to that yet.

There is one build dependency at the moment: Intel's hyperscan library (libhs). With that installed, it builds with GNU make.

A lot of these ideas were borrowed heavily from levee

Example

This is a basic HTTP server.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <crux.h>
#include <crux/net.h>
#include <crux/http.h>
#include <crux/filter.h>

struct server {
	const char *net;
	struct xfilter *filter;
	union xaddr addr;
	int fd;
};

struct client {
	struct server *server;
	union xaddr addr;
	int fd;
};

static const char response[] = 
	"HTTP/1.1 200 OK\r\n"
	"Content-Length: 6\r\n"
	"\r\n"
	"Hello\n"
	;

static void server(struct xhub *h, union xvalue val);
static void connection(struct xhub *h, union xvalue val);
static void intr(struct xhub *h, union xvalue val);
static void term(struct xhub *h, union xvalue val);

int
main(int argc, char *const *argv)
{
	// setup the server configuration
	struct server srv = {
		.fd = -1,
		.net = argc > 1 ? argv[1] : ":3333",
	};
	
	// create a filter
	// this is passed to the http parser to supress headers
	static const struct xfilter_expr filt[] = {
		{ "^[^X]", NULL, XFILTER_HTTP },
		{ "^X-Bar$", "^baz", XFILTER_HTTP },
		{ "^User-Agent$", "curl", XFILTER_HTTP },
	};

	struct xfilter_err err;
	int rc = xfilter_new(&srv.filter, filt, xlen(filt), XFILTER_ACCEPT, &err);
	if (rc < 0) {
		xerr_fabort(rc, "%s", err.message);
	}

	struct xhub *hub;
	xcheck(xhub_new(&hub));
	xspawn(hub, server, xptr(&srv));
	xhub_run(hub);
	xhub_free(&hub);
	return 0;
}

static void
server(struct xhub *h, union xvalue val)
{
	struct server *srv = val.ptr;
	int s = xcheck(xbind(srv->net, SOCK_STREAM, XREUSEADDR, &srv->addr));
	srv->fd = s;

	// spawn signal handling tasks
	xspawn(h, intr, xptr(srv));
	xspawn(h, term, xptr(srv));

	printf("[%d] listening on %s\n", getpid(), xaddrstr(&srv->addr));

	for (;;) {
		struct client *cli = malloc(sizeof(*cli));
		cli->fd = xcheck(xaccept(s, 0, -1, &cli->addr));
		cli->server = srv;
		printf("[%d] accepted from %s\n", getpid(), xaddrstr(&cli->addr));
		xspawn(h, connection, xptr(cli));
	}
}

static void
connection(struct xhub *h, union xvalue val)
{
	(void)h;

	struct client *cli = val.ptr;
	int fd = cli->fd;

	// clean up resources when the coroutine exits
	xdefer_close(fd);
	xdefer_free(cli);

	// http header fields may optionally be collected
	// the parser will not yield header fields when doing so
	struct xhttp_map *map;
	xhttp_map_new(&map);

	struct xhttp http;
	xhttp_init_request(&http, map, cli->server->filter);

	// we'll use an auto-managed buffer per connection
	struct xbuf *buf = xbuf(8000, true);
	while (xbuf_read(buf, fd, 4096, 2000) > 0) {
		for (;;) {
			// the http library is a pull parser that does not allocate
			ssize_t n = xcheck(xhttp_next(&http, buf));
			if (n == 0) { break; }

			// once we have a value, we can trim the buffer by the parsed amount
			// only do this once you no longer need the input values
			xbuf_trim(buf, n);
			
			// respond when complete and reset the parser for another request
			if (xhttp_is_done(&http)) {
				xhttp_reset(&http);
				xwrite(fd, response, sizeof(response)-1, -1);
			}
		}
	}
}

static void
intr(struct xhub *h, union xvalue val)
{
	struct server *srv = val.ptr;

	xsignal(SIGINT, XTIMEOUT_DETACH);
	printf("[%d] stopping %s\n", getpid(), xaddrstr(&srv->addr));
	xhub_stop(h);
}

static void
term(struct xhub *h, union xvalue val)
{
	(void)h;

	struct server *srv = val.ptr;

	xsignal(SIGTERM, XTIMEOUT_DETACH);
	printf("[%d] terminating %s\n", getpid(), xaddrstr(&srv->addr));
	xclose(val.i);
}

crux's People

Contributors

kalamay avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.