顯示具有 libevent 標籤的文章。 顯示所有文章
顯示具有 libevent 標籤的文章。 顯示所有文章

2011/06/20

HTTP server over libevent - sample code (2)

加入一些parse的function,可以得到request的method、URI、header、body、path

#include 
#include 
#include 
#include 
#include 

#define SVR_IP                         "127.0.0.1"
#define SVR_PORT                       8080

void dump_cb (struct evhttp_request *req, void *arg) {
    const char        *uri;
    struct evkeyvalq  *headers;
    struct evkeyval   *header;
    struct evbuffer   *evbuf;
    struct evhttp_uri *ev_uri;
    
    /* Get method */
    switch (evhttp_request_get_command(req)) {
        case EVHTTP_REQ_GET: 
            printf("Request method=[GET]\n");
            break;
        case EVHTTP_REQ_POST:
            printf("Request method=[POST]\n");
            break;
        default:
            printf("Request method=[others]\n");
            break;
    }
    
    /* Get path */
    uri = evhttp_request_get_uri(req);
    printf("Request uri=[%s]\n",  uri);
    
    /* Get header */
    printf("Headers:\n");
    headers = evhttp_request_get_input_headers(req);
    for (header = headers->tqh_first; header;
        header = header->next.tqe_next) {
        printf("  %s: %s\n", header->key, header->value);
    }
        
    /* Get body */
    evbuf = evhttp_request_get_input_buffer(req);
    printf("Body:\n");
    while (evbuffer_get_length(evbuf)) {
        int  size;
        char buf[128];
        
        memset(buf, 0, sizeof(buf));
        size = evbuffer_remove(evbuf, buf, sizeof(buf) - 1);
        printf("  %s\n", buf);
    }
    
    /* */
    ev_uri = evhttp_uri_parse(uri);
    if (ev_uri) {
        const char *path;
        char       *decoded_path;
        
        path = evhttp_uri_get_path(ev_uri);
        decoded_path = evhttp_uridecode(path, 0, NULL);
        printf("Request path=[%s]\n", decoded_path);
        
        /* Free resource */
        free(decoded_path);
        evhttp_uri_free(ev_uri);
    }
    
    /* Response */
    evhttp_send_reply(req, HTTP_OK, "OK", NULL);
}

int main (int argc, char **argv) {
    struct event_base *evbase;
    struct evhttp     *evhttp;

    /* Init event base */
    if ((evbase = event_base_new()) == NULL) {
        printf("event_base_new() failed\n");
        return -1;
    }

    /* Init evhttp */
    if ((evhttp = evhttp_new(evbase)) == NULL) {
        printf("evhttp_new() failed\n");
        return -1;
    }

    /* Set server IP, port */
    if (evhttp_bind_socket(evhttp, SVR_IP, SVR_PORT) == -1) {
        printf("evhttp_bind_socket() failed\n");
        return -1;
    } else {
        printf("Listening on [%s:%d]\n", SVR_IP, SVR_PORT);
    }
    
    /* Set timeout */
    evhttp_set_timeout(evhttp, 10);
    
    /* Set a callback for default path */
    evhttp_set_gencb(evhttp, dump_cb, NULL);

    /* Enter event loop */
    event_base_dispatch(evbase);

    /* Free resource */
    evhttp_free(evhttp);
    event_base_free(evbase);
    
    return 0;
}



Compile and run
$ gcc -g -Wall -o httpd httpd.c -levent -I/opt/libevent-2.0.12-stable/include/ -L/opt/libevent-2.0.12-stable/lib/
$ LD_LIBRARY_PATH=/opt/libevent-2.0.12-stable/lib ./httpd
Listening on [127.0.0.1:8080]

Request method=[GET]
Request uri=[/test.php?name=kevin&sex=male]
Headers:
  User-Agent: Wget/1.11.4 Red Hat modified
  Accept: */*
  Host: 127.0.0.1:8080
  Connection: Keep-Alive
Body:
Request path=[/test.php]

Request method=[POST]
Request uri=[/test.php]
Headers:
  User-Agent: Wget/1.11.4 Red Hat modified
  Accept: */*
  Host: 127.0.0.1:8080
  Connection: Keep-Alive
  Content-Type: application/x-www-form-urlencoded
  Content-Length: 19
Body:
  name=sylar&sex=male
Request path=[/test.php]


try to send a request to server...
# Get
$wget -q -O - 'http://127.0.0.1:8080/test.php?name=sylar&sex=male'

# Post
$wget -q -O - --post-data='name=sylar&sex=male' http://127.0.0.1:8080/test.php

2011/06/17

HTTP server over libevent - sample code (1)

使用libevent HTTP API寫一個簡易的server。

#include 
#include 
#include 

#define SVR_IP                         "127.0.0.1"
#define SVR_PORT                       8080

void specific_cb (struct evhttp_request *req, void *arg) {
    struct evbuffer *evbuf;
    
    if ((evbuf = evbuffer_new()) == NULL) {
        printf("evbuffer_new() failed");
        evhttp_send_reply(req, HTTP_INTERNAL, "Internal error", NULL);
        return;
    }

    /* Body */
    evbuffer_add_printf(evbuf, "It's work!");
    
    /* Response */
    evhttp_send_reply(req, HTTP_OK, "OK", evbuf);
    
    /* Free resource */
    evbuffer_free(evbuf);
}

void generic_cb (struct evhttp_request *req, void *arg) {

    /* Response */
    evhttp_send_reply(req, HTTP_NOTFOUND, "Not found", NULL);
}

int main (int argc, char **argv) {
    struct event_base *evbase;
    struct evhttp     *evhttp;

    /* Init event base */
    if ((evbase = event_base_new()) == NULL) {
        printf("event_base_new() failed\n");
        return -1;
    }

    /* Init evhttp */
    if ((evhttp = evhttp_new(evbase)) == NULL) {
        printf("evhttp_new() failed\n");
        return -1;
    }

    /* Set server IP, port */
    if (evhttp_bind_socket(evhttp, SVR_IP, SVR_PORT) == -1) {
        printf("evhttp_bind_socket() failed\n");
        return -1;
    } else {
        printf("Listening on [%s:%d]\n", SVR_IP, SVR_PORT);
    }
    
    /* Set a callback for specific path */
    if (evhttp_set_cb(evhttp, "/test", specific_cb, NULL) < 0) {
        printf("evhttp_set_cb() failed\n");
        return -1;
    }
    
    /* Set a callback for default path */
    evhttp_set_gencb(evhttp, generic_cb, NULL);

    /* Enter event loop */
    event_base_dispatch(evbase);

    /* Free resource */
    evhttp_free(evhttp);
    event_base_free(evbase);
    
    return 0;
}
Compile and run
$ gcc -g -Wall -o httpd httpd.c -levent -I/opt/libevent-2.0.12-stable/include/ -L/opt/libevent-2.0.12-stable/lib/
$ LD_LIBRARY_PATH=/opt/libevent-2.0.12-stable/lib ./httpd
Listening on [127.0.0.1:8080]
try to send a request to server...
$ wget -q -O - http://127.0.0.1:8080/test
$ wget -S -O - http://127.0.0.1:8080/xxx
不知道為什麼我用2.0.10-stable版本跑會有下列warning,用2.0.12-stable就沒問題了。
[warn] evhttp_connection_base_new: bufferevent_new failed: No such file or directory
[warn] evhttp_get_request: cannot get connection on 9: No such file or directory

2011/06/16

Echo server over libevent - sample code

一個簡易的echo server,使用了bufferevent。

Bufferevent
Bufferevents are higher level than evbuffers: each has an underlying evbuffer for reading and one for writing, and callbacks that are invoked under certain circumstances.

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event.h>
#include <event2/listener.h>

#define SVR_IP                         "127.0.0.1"
#define SVR_PORT                       10000
#define BUFF_SIZE                      1024

static void read_cb(struct bufferevent *bev, void *ctx) {
    char buff[BUFF_SIZE];
    int len;

    memset(buff, 0, sizeof(buff));

    /* Read data */
    len = bufferevent_read(bev, buff, sizeof(buff));
    printf("read: len=[%d] data=[%s]\n", len, buff);

    /* Write data */
    bufferevent_write(bev, buff, len);
}

static void write_cb(struct bufferevent *bev, void *ctx) {
    printf("write finished\n");
}

static void event_cb(struct bufferevent *bev, short events, void *ctx) {
    if (events & BEV_EVENT_EOF) {
        printf("client disconnect\n");
        bufferevent_free(bev);
    } else if (events & BEV_EVENT_TIMEOUT) {
        printf("client timeout\n");
        bufferevent_free(bev);
    } else {
        /* Other case, maybe error occur */
        bufferevent_free(bev);
    }
}

static void accept_cb(struct evconnlistener *lev, evutil_socket_t fd,
        struct sockaddr *sa, int socklen, void *ctx) {
    struct event_base *evbase = ctx;
    struct bufferevent *bev;
    struct timeval tv;

    printf("client connect from [%s:%u] over fd [%d]\n", 
            inet_ntoa(((struct sockaddr_in *) sa)->sin_addr), 
            (unsigned short) ntohs(((struct sockaddr_in *) sa)->sin_port), fd);

    /* Create socket-based buffer event */
    bev = bufferevent_socket_new(evbase, fd, BEV_OPT_CLOSE_ON_FREE);
    if (bev == NULL) {
        printf("bufferevent_socket_new() failed\n");
        evutil_closesocket(fd);
        return;
    }

    /* Set up callback function */
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);

    /* Set up timeout for data read */
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    bufferevent_set_timeouts(bev, &tv, NULL);

    /* Enable read event */
    bufferevent_enable(bev, EV_READ);
}

int main(void) {
    struct event_base *evbase;
    struct sockaddr_in sin;
    struct evconnlistener *lev;

    /* Initialize event base */
    if ((evbase = event_base_new()) == NULL) {
        printf("event_base_new() failed\n");
        return -1;
    }

    /* Set up server address */
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(SVR_IP);
    sin.sin_port = htons(SVR_PORT);

    /* Bind socket */
    lev = evconnlistener_new_bind(evbase, accept_cb, evbase, 
            LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1, 
            (struct sockaddr *) &sin, sizeof(sin));

    if (lev == NULL) {
        printf("bind() failed\n");
        return -1;
    } else {
        printf("bind to [%s:%u] successfully\n", SVR_IP, SVR_PORT);
    }

    /* Enter event loop */
    event_base_dispatch(evbase);

    return 0;
}


編譯 (指定library path)
gcc -g -Wall -o server server.c -levent -I/opt/libevent-2.0.10-stable/include/ -L/opt/libevent-2.0.10-stable/lib/

2011/05/19

UDP server over libevent - sample code

基本的sample code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event.h>
#include <event2/listener.h>

#define SVR_IP                          "127.0.0.1"
#define SVR_PORT                        10000
#define BUF_SIZE                        1024

void read_cb(int fd, short event, void *arg) {
    char                buf[BUF_SIZE];
    int                 len;
    int                 size = sizeof(struct sockaddr);
    struct sockaddr_in  client_addr;

    memset(buf, 0, sizeof(buf));
    len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &size);

    if (len == -1) {
        perror("recvfrom()");
    } else if (len == 0) {
        printf("Connection Closed\n");
    } else {
        printf("Read: len [%d] - content [%s]\n", len, buf);
        
        /* Echo */
        sendto(fd, buf, len, 0, (struct sockaddr *)&client_addr, size);
    }
}

int bind_socket(struct event *ev) {
    int                 sock_fd;
    int                 flag = 1;
    struct sockaddr_in  sin;

    /* Create endpoint */
    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket()");
        return -1;
    }

    /* Set socket option */
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {
        perror("setsockopt()");
        return 1;
    }

    /* Set IP, port */
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(SVR_IP);
    sin.sin_port = htons(SVR_PORT);

    /* Bind */
    if (bind(sock_fd, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) {
        perror("bind()");
        return -1;
    } else {
        printf("bind() success - [%s] [%u]\n", SVR_IP, SVR_PORT);
    }

    /* Init one event and add to active events */
    event_set(ev, sock_fd, EV_READ | EV_PERSIST, &read_cb, NULL);
    if (event_add(ev, NULL) == -1) {
        printf("event_add() failed\n");
    }

    return 0;
}

int main(void) {
    struct event  ev;

    /* Init. event */
    if (event_init() == NULL) {
        printf("event_init() failed\n");
        return -1;
    }

    /* Bind socket */
    if (bind_socket(&ev) != 0) {
        printf("bind_socket() failed\n");
        return -1;
    }

    /* Enter event loop */
    event_dispatch();

    return 0;
}