Skip to content

Commit

Permalink
Added another version without evhttp build in
Browse files Browse the repository at this point in the history
  • Loading branch information
berviantoleo committed Sep 11, 2017
1 parent 1b381c6 commit d5a7b14
Show file tree
Hide file tree
Showing 3 changed files with 350 additions and 3 deletions.
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
dest_directory = /opt/mws/
program_name = mini-web-server
small_program = mws

all:
gcc cJSON.c server-evhttp.c -o $(program_name) -levent

small:
gcc cJSON.c server.c -o $(small_program) -levent

install:
gcc cJSON.c server-evhttp.c -o $(program_name) -levent
gcc cJSON.c server.c -o $(small_program) -levent
test -d $(dest_directory) || mkdir -p $(dest_directory) && cp -R . $(dest_directory)
ln -sf $(dest_directory)$(program_name) /usr/bin/$(program_name)
ln -sf $(dest_directory)$(program_name) /usr/bin/$(program_name)
ln -sf $(dest_directory)$(small_program) /usr/bin/$(small_program)
rm mini-web-server

rm mws

remove:
rm -Rf $(dest_directory)
rm -f /usr/bin/$(program_name)

rm -f /usr/bin/$(small_program)
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,36 @@ Memori yang digunakan sekitar `215 MB`.

Respons time yang lebih kecil dibandingkan dengan apache dan nginx, penggunaan memori pun masih lebih besar.

## Tanpa Menggunakan Build in Evhttp

### Compile

Jika hanya ingin meng-compile dapat menggunakan perintah berikut.

```
make small
```

### Installasi

Dapat menggunakan:

```
make install
```

Hal ini akan memberikan instalasi mini-web-server dan mws.

### Jalankan

```
./mws
```

### Konfigurasi

File konfigurasi yang digunakan sama dengan mini-web-server.

# About

Bervianto Leo P - 13514047
Expand Down
310 changes: 310 additions & 0 deletions server.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
#include <event.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include "cJSON.h"

#define MAX_PATH_LEN 1024
#define DEFAULT_PORT 12345

char uri_root[512];
char doc_root[100];
char name_server[100];
unsigned short port;

struct client {
int fd;
struct bufferevent *buf_ev;
};

int set_nonblock(int fd) {
int flags;

flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
}

void err(int exit_code, char * error_text) {
perror(error_text);
exit(1);
}

void parse_request(char* request, char** output_path) {
char *ptr;
int path_len;

// don't care about the type of the request, just find the second arg
ptr = request;
while (*ptr != 0 && *ptr != ' ') {
++ptr;
}
if (*ptr == 0) {
*output_path = NULL;
return;
}

++ptr;
path_len = 0;
while (ptr[path_len] != 0 &&
ptr[path_len] != ' ' &&
ptr[path_len] != '#' &&
ptr[path_len] != '&') {
++path_len;
}
*output_path = (char*) calloc(MAX_PATH_LEN, sizeof(char));
strcpy(*output_path, doc_root);
strncat(*output_path, ptr, path_len);
}

int get_default_file(char** path) {
struct stat st;
int err;

err = stat(*path, &st);
if (err < 0) {
return err;
} else {
if (S_ISDIR(st.st_mode)) {
if ((*path)[strlen(*path) - 1] == '/') {
strcat(*path, "index.html");
} else {
strcat(*path, "/index.html");
}
err = stat(*path, &st);
if (err < 0) {
return err;
} else if (S_ISDIR(st.st_mode)) {
return -1;
} else {
return st.st_size;
}
} else {
return st.st_size;
}
}
}

void add_404_response(struct evbuffer *event_response) {
evbuffer_add_printf(event_response, "HTTP/1.1 404 Not Found\n");
evbuffer_add_printf(event_response, "Content-Type: text/plain\n");
evbuffer_add_printf(event_response, "Content-Length: 9\n");
evbuffer_add_printf(event_response, "\n");
evbuffer_add_printf(event_response, "Not Found\n");
}

void add_200_response(struct evbuffer *event_response, char* path, int fsize) {
FILE* f;

evbuffer_add_printf(event_response, "HTTP/1.1 200 OK\n");
evbuffer_add_printf(event_response, "Content-Type: text/html\n");
evbuffer_add_printf(event_response, "Content-Length: %d\n", fsize);
evbuffer_add_printf(event_response, "\n");

printf("Loading file %s ...\n", path);
f = fopen(path, "r");
evbuffer_add_file(event_response, fileno(f), 0, fsize);
}

void buf_read_callback(struct bufferevent *incoming, void *arg) {
struct evbuffer *event_response;
char *request, *path, *response;
int fsize;

request = evbuffer_readln(incoming->input, NULL, EVBUFFER_EOL_ANY);
if (request == NULL) {
return;
}
// skip the rest of request
while (evbuffer_readln(incoming->input, NULL, EVBUFFER_EOL_ANY) != NULL);
event_response = evbuffer_new();

printf("Processing request: %s\n", request);
parse_request(request, &path);
fsize = get_default_file(&path);

if (fsize < 0) {
add_404_response(event_response);
} else {
add_200_response(event_response, path, fsize);
}

bufferevent_write_buffer(incoming, event_response);
evbuffer_free(event_response);
free(request);
free(path);
}

void buf_error_callback(struct bufferevent *bev, short what, void *arg) {
struct client *client = (struct client *)arg;
bufferevent_free(client->buf_ev);
close(client->fd);
free(client);
}

void accept_callback(int fd, short ev, void *arg) {
int client_fd;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
struct client *client;
struct timeval *timeout;

client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0) {
printf("%s\n", "Client: accept() failed");
return;
}

set_nonblock(client_fd);

client = calloc(1, sizeof(*client));
if (client == NULL) {
err(1, "Failed to allocate memory on client");
}
client->fd = client_fd;

client->buf_ev = bufferevent_new(
client_fd,
buf_read_callback,
NULL,
buf_error_callback,
client);

bufferevent_enable(client->buf_ev, EV_READ);

timeout = (struct timeval*)malloc(sizeof(struct timeval*));
timeout->tv_usec = 100000;
bufferevent_set_timeouts(client->buf_ev, timeout, NULL);
}

int initialize_socket() {
int sock;
struct sockaddr_in serv_addr;

if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0) {
close(sock);
err(1, "Cannot open socket\n");
}

int reusable = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reusable, sizeof (reusable)) == -1) {
err(1, "Cannot set option\n");
}

bzero((char *) &serv_addr, sizeof (serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);

if (bind(sock, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
close(sock);
err(1, "Failed to bind socket\n");
}

if (listen(sock, 5) < 0) {
close(sock);
err(1, "Failed to listen to socket\n");
}

set_nonblock(sock);

return sock;
}

char * read_file(char * filename) {
char *buffer = NULL;
int string_size, read_size;
FILE *handler = fopen(filename, "r");

if (handler)
{
// Seek the last byte of the file
fseek(handler, 0, SEEK_END);
// Offset from the first to the last byte, or in other words, filesize
string_size = ftell(handler);
// go back to the start of the file
rewind(handler);

// Allocate a string that can hold it all
buffer = (char*) malloc(sizeof(char) * (string_size + 1) );

// Read it all in one operation
read_size = fread(buffer, sizeof(char), string_size, handler);

// fread doesn't set it so put a \0 in the last position
// and buffer is now officially a string
buffer[string_size] = '\0';

if (string_size != read_size)
{
// Something went wrong, throw away the memory and set
// the buffer to NULL
free(buffer);
buffer = NULL;
}

// Always remember to close the file.
fclose(handler);
}

return buffer;
}

void configure() {
char * config_location = "config/config.json";
char * json_string = read_file(config_location);
cJSON * root = cJSON_Parse(json_string);
cJSON * port_item = cJSON_GetObjectItemCaseSensitive(root, "port");
cJSON * directory_item = cJSON_GetObjectItemCaseSensitive(root, "directory");
cJSON * name_server_item = cJSON_GetObjectItemCaseSensitive(root, "name_server");
int port_value = 0;
if (cJSON_IsNumber(port_item)) {
port_value = port_item->valueint;
if (port_value > 0) {
port = port_value;
} else {
port = DEFAULT_PORT;
}
}
if (cJSON_IsString(directory_item)) {
strcpy(doc_root, directory_item->valuestring);
} else {
strcpy(doc_root, "htdocs");
}
if (cJSON_IsString(name_server_item)) {
strcpy(name_server, name_server_item->valuestring);
} else {
strcpy(name_server, "localhost");
}
cJSON_Delete(root);
}

void show_license() {
char * license = read_file("LICENSE-TEXT");
printf("%s\n", license);
}

int main(int argc, char **argv) {
configure();
show_license();

int sock;
struct event accept_event;
sock = initialize_socket();

event_init();
event_set(&accept_event, sock, EV_READ|EV_PERSIST, accept_callback, NULL);
event_add(&accept_event, NULL);
event_dispatch();

close(sock);

return 0;
}

0 comments on commit d5a7b14

Please sign in to comment.