Skip to content

Instantly share code, notes, and snippets.

@indutny
Created August 27, 2012 15:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save indutny/2bbef0db7b225d3e9e5c to your computer and use it in GitHub Desktop.
Save indutny/2bbef0db7b225d3e9e5c to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
/* kqueue */
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
/* sockets */
#include <sys/socket.h>
#include <netinet/in.h>
#define EVENT_COUNT 1024
typedef struct client_s client_t;
struct client_s {
int fd;
char buff[10 * 1024];
int headers;
int sent;
};
int create_server(int port) {
int r;
int fd;
int enable;
sockaddr_in addr;
fd = socket(AF_INET, SOCK_STREAM, 0);
assert(fd >= 0);
enable = 1;
r = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable));
assert(r == 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
r = bind(fd, (sockaddr*) &addr, sizeof(addr));
assert(r == 0);
r = listen(fd, 1024);
assert(r == 0);
return fd;
}
void accept_incoming(int kq, int server) {
sockaddr_in addr;
socklen_t addr_size;
struct kevent events[2];
client_t* client;
int r;
int fd;
int enable;
addr_size = sizeof(addr);
fd = accept(server, (sockaddr*) &addr, &addr_size);
assert(fd >= 0);
client = (client_t*) malloc(sizeof(client_t));
assert(client != NULL);
memset(client, 0, sizeof(*client));
client->fd = fd;
/* Make socket non-blocking */
enable = 1;
do {
r = ioctl(fd, FIONBIO, &enable);
} while (r == -1 && errno == EINTR);
/* Watch new connection */
EV_SET(&events[0], fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, client);
EV_SET(&events[1], fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, client);
r = kevent(kq, events, 2, NULL, 0, NULL);
assert(r == 0);
}
void disconnect_client(int kq, client_t* client) {
struct kevent events[2];
int r;
/* Remove event from kqueue */
EV_SET(&events[0],
client->fd,
EVFILT_READ,
EV_DELETE | EV_DISABLE,
0,
0,
client);
EV_SET(&events[1],
client->fd,
EVFILT_WRITE,
EV_DELETE | EV_DISABLE,
0,
0,
client);
r = kevent(kq, events, 2, NULL, 0, NULL);
assert(r == 0);
/* Close socket */
close(client->fd);
/* Free memory */
free(client);
}
void handle_read(int kq, client_t* client, int bytes) {
char buff[1024];
size_t r;
r = read(client->fd, buff, bytes);
assert(r >= 0);
}
void handle_write(int kq, client_t* client, int bytes) {
if (client->sent) return;
client->sent = 1;
const char* res = "HTTP/1.1 200 Ok\r\n\r\nhello";
write(client->fd, res, strlen(res));
shutdown(client->fd, SHUT_WR);
}
int main() {
int r;
int kq;
int server;
int numevents;
int i;
int recv_signal;
struct kevent events[EVENT_COUNT];
struct sigaction sigact;
/* Ignore SIGPIPE */
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigact, 0);
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = SIG_IGN;
sigaction(SIGINT, &sigact, 0);
kq = kqueue();
assert(kq >= 0);
server = create_server(8080);
fprintf(stdout, "Server fd: %d\n", server);
/* Watch incoming connections */
EV_SET(&events[0], server, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
/* Watch signals */
EV_SET(&events[1], SIGINT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, 0);
r = kevent(kq, events, 2, NULL, 0, NULL);
assert(r == 0);
recv_signal = 0;
while (!recv_signal) {
numevents = kevent(kq, NULL, 0, events, EVENT_COUNT, NULL);
if (numevents < 0) {
if (errno == EINTR) continue;
fprintf(stderr, "Error: %d\n", errno);
abort();
}
for (i = 0; i < numevents; i++) {
/* Skip errors for now */
if (events[i].flags & EV_ERROR) continue;
/* Handle signals */
if (events[i].filter == EVFILT_SIGNAL) {
recv_signal = 1;
break;
}
/* Handle incoming connections */
if (events[i].filter == EVFILT_READ && events[i].ident == server) {
while (events[i].data--) {
accept_incoming(kq, server);
}
}
/* XXX: Why is it happening? */
if (events[i].udata == NULL) continue;
if (events[i].filter == EVFILT_READ) {
if (events[i].flags & EV_EOF) {
int j;
disconnect_client(kq, (client_t*) events[i].udata);
/* Ignore events for closed fd */
for (j = i + 1; j < numevents; j++) {
if (events[i].ident == events[j].ident) events[j].udata = NULL;
}
continue;
}
handle_read(kq, (client_t*) events[i].udata, events[i].data);
} else if (events[i].filter == EVFILT_WRITE) {
assert(events[i].udata != NULL);
handle_write(kq, (client_t*) events[i].udata, events[i].data);
} else {
fprintf(stderr, "Unexpected filter: %d\n", events[i].filter);
abort();
}
}
}
fprintf(stdout, "\nClosing stuff\n");
close(server);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment