├── Makefile ├── README ├── include ├── common.h ├── conf.h ├── logger.h ├── server.h └── worker.h └── src ├── conf.c ├── logger.c ├── pulsar.c ├── server.c └── worker.c /Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: 2 | .SUFFIXES: .c .o 3 | 4 | .PHONY: clean 5 | 6 | CC = gcc 7 | CFLAGS = -Wall -Iinclude 8 | LDFLAGS = -levent -pthread 9 | SRCDIR = src 10 | OBJDIR = obj 11 | SRCS = $(addprefix $(SRCDIR)/, logger.c conf.c worker.c server.c pulsar.c) 12 | OBJS = $(addprefix $(OBJDIR)/, logger.o conf.o worker.o server.o pulsar.o) 13 | EXE = pulsar 14 | 15 | $(EXE): $(OBJS) 16 | $(CC) $(CFLAGS) $(OBJS) -o $(EXE) $(LDFLAGS) 17 | 18 | $(OBJDIR)/%.o: $(SRCDIR)/%.c 19 | $(CC) $(CFLAGS) -c $< -o $@ 20 | 21 | clean: 22 | rm $(OBJS) $(EXE) -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | About: 2 | ------ 3 | pulsar is a libevent based multi-threaded web server 4 | 5 | Build: 6 | ------ 7 | git clone git://github.com/abhinavsingh/pulsar.git 8 | cd pulsar/ 9 | make 10 | ./pulsar 11 | 12 | Help: 13 | ----- 14 | ./pulsar -h -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common.h 3 | * 4 | * Created on: Apr 22, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #ifndef COMMON_H_ 9 | #define COMMON_H_ 10 | 11 | 12 | typedef struct _server server; 13 | typedef struct _worker worker; 14 | typedef struct _logger logger; 15 | typedef struct _conf conf; 16 | 17 | 18 | #endif /* COMMON_H_ */ 19 | -------------------------------------------------------------------------------- /include/conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * conf.h 3 | * 4 | * Created on: Apr 3, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #ifndef CONF_H_ 9 | #define CONF_H_ 10 | 11 | #include "logger.h" 12 | #include "common.h" 13 | 14 | struct _conf { 15 | char *ip; 16 | unsigned short port; 17 | int workers; 18 | }; 19 | 20 | void 21 | conf_free(); 22 | 23 | conf * 24 | conf_new(char *ip, unsigned short port, int workers); 25 | 26 | #endif /* CONF_H_ */ 27 | -------------------------------------------------------------------------------- /include/logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * logger.h 3 | * 4 | * Created on: Apr 7, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #ifndef LOGGER_H_ 9 | #define LOGGER_H_ 10 | 11 | #include "common.h" 12 | 13 | typedef enum { 14 | PULSAR_ERROR = 0, 15 | PULSAR_WARNING, 16 | PULSAR_NOTICE, 17 | PULSAR_INFO, 18 | PULSAR_DEBUG 19 | } log_level; 20 | 21 | struct _logger { 22 | pid_t self; 23 | int fd; 24 | 25 | char *logfile; 26 | log_level verbosity; 27 | }; 28 | 29 | void 30 | log_free(logger *log); 31 | 32 | logger * 33 | log_new(char *logfile, log_level level); 34 | 35 | void 36 | log_it(logger *log, log_level level, const char *body); 37 | 38 | #endif /* LOGGER_H_ */ 39 | -------------------------------------------------------------------------------- /include/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * server.h 3 | * 4 | * Created on: Apr 3, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #ifndef SERVER_H_ 9 | #define SERVER_H_ 10 | 11 | #define SERVER_NAME "pulsar" 12 | #define SERVER_VERSION "0.1" 13 | 14 | #include "common.h" 15 | 16 | struct _server { 17 | /* config */ 18 | conf *cfg; 19 | 20 | /* socket */ 21 | int fd; 22 | struct event_base *base; 23 | struct event *signal; 24 | 25 | /* workers */ 26 | worker **w; 27 | 28 | /* log */ 29 | logger *log; 30 | }; 31 | 32 | server * 33 | server_new(conf *cfg, logger *log); 34 | 35 | void 36 | server_start(server *s); 37 | 38 | #endif /* SERVER_H_ */ 39 | -------------------------------------------------------------------------------- /include/worker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * worker.h 3 | * 4 | * Created on: Apr 5, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #ifndef WORKER_H_ 9 | #define WORKER_H_ 10 | 11 | #include 12 | #include "common.h" 13 | 14 | struct _worker { 15 | pthread_t t; 16 | 17 | struct event_base *base; 18 | struct evhttp *http; 19 | 20 | server *s; 21 | }; 22 | 23 | worker * 24 | worker_new(server *s); 25 | 26 | void 27 | worker_start(worker *w); 28 | 29 | void 30 | worker_free(worker *w); 31 | 32 | #endif /* WORKER_H_ */ 33 | -------------------------------------------------------------------------------- /src/conf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * conf.c 3 | * 4 | * Created on: Apr 7, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #include 9 | 10 | #include "conf.h" 11 | 12 | void 13 | conf_free(conf *cfg) { 14 | free(cfg->ip); 15 | free(cfg); 16 | } 17 | 18 | conf * 19 | conf_new(char *ip, unsigned short port, int workers) { 20 | conf *cfg; 21 | cfg = calloc(1, sizeof(conf)); 22 | 23 | cfg->ip = ip; 24 | cfg->port = port; 25 | cfg->workers = workers; 26 | 27 | return cfg; 28 | } 29 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * logger.c 3 | * 4 | * Created on: Apr 7, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include "server.h" 20 | #include "conf.h" 21 | 22 | void 23 | log_free(logger *log) { 24 | free(log); 25 | } 26 | 27 | logger * 28 | log_new(char *logfile, log_level level) { 29 | logger *log; 30 | log = (logger *)calloc(1, sizeof(logger)); 31 | 32 | log->self = getpid(); 33 | log->logfile = logfile; 34 | log->verbosity = level; 35 | 36 | if(log->logfile) { 37 | log->fd = open(log->logfile, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR|S_IWUSR); 38 | } 39 | else { 40 | /* stderr */ 41 | log->fd = 2; 42 | } 43 | 44 | return log; 45 | } 46 | 47 | void 48 | log_it(logger *log, log_level level, const char *body) { 49 | const char *c = ".-*#"; 50 | 51 | time_t now; 52 | char time_buf[64]; 53 | 54 | size_t sz; 55 | char msg[124]; 56 | 57 | char line[256]; 58 | int line_sz, ret; 59 | 60 | if(level > log->verbosity) return; 61 | if(!log->fd) return; 62 | 63 | /* limit max log size */ 64 | sz = strlen(body); 65 | snprintf(msg, sz + 1 > sizeof(msg) ? sizeof(msg) : sz + 1, "%s", body); 66 | 67 | /* time */ 68 | now = time(NULL); 69 | strftime(time_buf, sizeof(time_buf), "%d %b %H:%M:%S", localtime(&now)); 70 | 71 | /* out line */ 72 | line_sz = snprintf(line, sizeof(line), "[%d] %s %d %s\n", (int)log->self, time_buf, c[level], msg); 73 | 74 | /* write to log and flush to disk. */ 75 | ret = write(log->fd, line, line_sz); 76 | ret = fsync(log->fd); 77 | 78 | (void)ret; 79 | } 80 | -------------------------------------------------------------------------------- /src/pulsar.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulsar.c 3 | * 4 | * Created on: Apr 3, 2012 5 | * Author: abhinavsingh 6 | * 7 | * Memory Leak Check: 8 | * ------------------- 9 | * valgrind --leak-check=full --show-reachable=yes ./pulsar 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "conf.h" 18 | #include "server.h" 19 | 20 | void 21 | print_usage(char *exe) { 22 | printf("Usage: %s [-p port] [-w workers] [-h]\n\n" 23 | "Options:\n" 24 | " -p port : tcp port number to listen on (default: 9090)\n" 25 | " -w workers : number of worker threads to start for accepting connections (default: 4)\n" 26 | " -v verbosity : 0 <= verbosity <= 4 (default: 4)\n" 27 | " -l logfile : file to log into (default: stderr)\n" 28 | " -h : display this help message\n", exe); 29 | } 30 | 31 | int 32 | main(int argc, char *argv[]) { 33 | int opt; 34 | 35 | server *s; 36 | conf *cfg; 37 | logger *log; 38 | 39 | /* defaults */ 40 | log = log_new("log/pulsar.log", PULSAR_DEBUG); 41 | cfg = conf_new(strdup("0.0.0.0"), 9090, 4); 42 | 43 | /* read input options */ 44 | while((opt = getopt(argc,argv,"p:w:v:l:h")) != -1) { 45 | switch(opt) { 46 | case 'p': 47 | cfg->port = atoi(optarg); 48 | break; 49 | case 'w': 50 | cfg->workers = atoi(optarg); 51 | break; 52 | case 'v': 53 | log->verbosity = atoi(optarg); 54 | break; 55 | case 'l': 56 | log->logfile = optarg; 57 | break; 58 | case 'h': 59 | print_usage(argv[0]); 60 | exit(EXIT_FAILURE); 61 | case '?': 62 | print_usage(argv[0]); 63 | exit(EXIT_FAILURE); 64 | } 65 | } 66 | 67 | s = server_new(cfg, log); 68 | server_start(s); 69 | 70 | return EXIT_SUCCESS; 71 | } 72 | -------------------------------------------------------------------------------- /src/server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * server.c 3 | * 4 | * Created on: Apr 3, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "server.h" 21 | #include "worker.h" 22 | #include "conf.h" 23 | 24 | int 25 | server_setup_socket(const char *ip, short port) { 26 | int fd, reuse = 1, ret; 27 | struct sockaddr_in addr; 28 | 29 | addr.sin_family = AF_INET; 30 | addr.sin_port = htons(port); 31 | memset(&(addr.sin_addr), 0, sizeof(addr.sin_addr)); 32 | addr.sin_addr.s_addr = inet_addr(ip); 33 | 34 | fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 35 | assert(fd != -1); 36 | 37 | ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); 38 | assert(ret != -1); 39 | 40 | ret = evutil_make_socket_nonblocking(fd); 41 | assert(ret != -1); 42 | 43 | ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); 44 | assert(ret != -1); 45 | 46 | ret = listen(fd, SOMAXCONN); 47 | assert(ret != -1); 48 | 49 | return fd; 50 | } 51 | 52 | server * 53 | server_new(conf *cfg, logger *log) { 54 | int i; 55 | 56 | server *s; 57 | s = calloc(1, sizeof(server)); 58 | 59 | /* read cfg file */ 60 | s->cfg = cfg; 61 | 62 | /* log */ 63 | s->log = log; 64 | 65 | /* setup workers */ 66 | s->w = calloc(s->cfg->workers, sizeof(worker *)); 67 | for(i=0; icfg->workers; i++) { 68 | s->w[i] = worker_new(s); 69 | } 70 | log_it(s->log, PULSAR_DEBUG, "workers initialized ..."); 71 | 72 | return s; 73 | } 74 | 75 | void 76 | server_free(server *s) { 77 | int i; 78 | 79 | /* shutdown worker threads */ 80 | for(i=0; icfg->workers; i++) { 81 | worker_free(s->w[i]); 82 | } 83 | 84 | /* free */ 85 | event_del(s->signal); 86 | event_free(s->signal); 87 | event_base_free(s->base); 88 | close(s->fd); 89 | free(s->w); 90 | conf_free(s->cfg); 91 | log_free(s->log); 92 | free(s); 93 | } 94 | 95 | static void 96 | server_sig_handler(evutil_socket_t fd, short event, void *arg) { 97 | server *s = arg; 98 | server_free(s); 99 | exit(EXIT_SUCCESS); 100 | } 101 | 102 | void 103 | server_start(server *s) { 104 | int i; 105 | 106 | s->base = event_base_new(); 107 | s->signal = event_new(s->base, SIGINT, EV_SIGNAL|EV_PERSIST, &server_sig_handler, s); 108 | event_add(s->signal, NULL); 109 | 110 | s->fd = server_setup_socket(s->cfg->ip, s->cfg->port); 111 | assert(s->fd != -1); 112 | 113 | /* start workers */ 114 | for(i=0; icfg->workers; i++) { 115 | worker_start(s->w[i]); 116 | } 117 | 118 | event_base_dispatch(s->base); 119 | 120 | server_free(s); 121 | exit(EXIT_SUCCESS); 122 | } 123 | -------------------------------------------------------------------------------- /src/worker.c: -------------------------------------------------------------------------------- 1 | /* 2 | * worker.c 3 | * 4 | * Created on: Apr 5, 2012 5 | * Author: abhinavsingh 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "server.h" 17 | #include "worker.h" 18 | 19 | void 20 | worker_handler(struct evhttp_request *req, void *arg) { 21 | //worker *w = arg; 22 | int ret; 23 | 24 | struct evhttp_connection *conn; 25 | enum evhttp_cmd_type method; 26 | const struct evhttp_uri *uri; 27 | struct evkeyvalq *headers; 28 | 29 | const char *host, *uri_host, *uri_path, *uri_query, *uri_user_info, *uri_scheme; 30 | ev_uint16_t port; 31 | char *addr; 32 | int uri_port; 33 | 34 | struct evkeyval *header; 35 | struct evkeyvalq *out_headers; 36 | struct evbuffer *buffer; 37 | 38 | /* fetch request data */ 39 | conn = evhttp_request_get_connection(req); 40 | host = evhttp_request_get_host(req); 41 | evhttp_connection_get_peer(conn, &addr, &port); 42 | method = evhttp_request_get_command(req); 43 | uri = evhttp_request_get_evhttp_uri(req); 44 | headers = evhttp_request_get_input_headers(req); 45 | uri_host = evhttp_uri_get_host(uri); 46 | uri_path = evhttp_uri_get_path(uri); 47 | uri_query = evhttp_uri_get_query(uri); 48 | uri_user_info = evhttp_uri_get_userinfo(uri); 49 | uri_scheme = evhttp_uri_get_scheme(uri); 50 | uri_port = evhttp_uri_get_port(uri); 51 | 52 | /* simple log */ 53 | /*for(header=headers->tqh_first; header; header=header->next.tqe_next) { 54 | printf("%s: %s\n", header->key, header->value); 55 | }*/ 56 | 57 | /*printf("addr:%s, port:%d, host:%s, method:%d, host:%s, path:%s, query:%s, user info:%s, scheme:%s, port:%d\n", 58 | addr, port, host, method, uri_host, uri_path, uri_query, uri_user_info, uri_scheme, uri_port);*/ 59 | 60 | /* prepare out headers */ 61 | out_headers = evhttp_request_get_output_headers(req); 62 | evhttp_add_header(out_headers, "Content-Type", "text/html; charset=UTF-8"); 63 | evhttp_add_header(out_headers, "Server", SERVER_NAME); 64 | 65 | /* prepare out buffer */ 66 | buffer = evbuffer_new(); 67 | ret = evbuffer_add_printf(buffer, "%s", "hello world!"); 68 | assert(ret != -1); 69 | 70 | /* reply */ 71 | evhttp_send_reply(req, HTTP_OK, "OK", buffer); 72 | 73 | evbuffer_free(buffer); 74 | } 75 | 76 | void 77 | worker_free(worker *w) { 78 | int i; 79 | void *res; 80 | 81 | i = pthread_cancel(w->t); 82 | assert(i == 0); 83 | 84 | i = pthread_join(w->t, &res); 85 | assert(i == 0); 86 | assert(res == PTHREAD_CANCELED); 87 | 88 | evhttp_free(w->http); 89 | event_base_free(w->base); 90 | free(w); 91 | } 92 | 93 | static void * 94 | worker_main(void *arg) { 95 | int ret; 96 | worker *w = arg; 97 | 98 | w->base = event_base_new(); 99 | assert(w->base != NULL); 100 | 101 | w->http = evhttp_new(w->base); 102 | assert(w->http != NULL); 103 | 104 | ret = evhttp_accept_socket(w->http, w->s->fd); 105 | assert(ret == 0); 106 | 107 | evhttp_set_gencb(w->http, &worker_handler, w); 108 | 109 | ret = event_base_dispatch(w->base); 110 | assert(ret == 0); 111 | 112 | evhttp_free(w->http); 113 | event_base_free(w->base); 114 | 115 | return NULL; 116 | } 117 | 118 | worker * 119 | worker_new(server *s) { 120 | worker *w; 121 | w = calloc(1, sizeof(worker)); 122 | 123 | w->s = s; 124 | 125 | return w; 126 | } 127 | 128 | void 129 | worker_start(worker *w) { 130 | pthread_create(&w->t, NULL, worker_main, w); 131 | } 132 | --------------------------------------------------------------------------------