Skip to content

Commit

Permalink
Release 1.0.0: Etag support, logging and basic features
Browse files Browse the repository at this point in the history
  • Loading branch information
MrOnlineCoder committed Jan 28, 2017
1 parent efa1e92 commit 01f9f7a
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ CFLAGS=-std=c99 -o shockd -w
WIN32_LIBS=-lws2_32
LINUX_LIBS=-pthread

SRC=main.c request.c response.c error.c config_file.c
SRC=main.c request.c response.c error.c config_file.c cache.c log.c

all:
@echo "Use make win32 to build shockd for Windows"
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ Features

* Serves HTML, CSS, JS, images, executables or any other files

* Logs all requests and errors to file

* Multi-threading (every new connection is processed in new thread)

* It is fast enough for home usage :D (but not as nginx for example)
* HTTP ETag cache support

* It is fast enough for home usage :D

Building
--------
Expand Down Expand Up @@ -49,11 +53,11 @@ TODO

* POST body parser

* Add cache support
* ~~Add cache support~~

* Simple module system?

* Logging to files
* ~~Logging to files~~

How server handles HTTP request
-------------------------------
Expand Down
15 changes: 15 additions & 0 deletions config_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
#include "stdio.h"
#include "stdlib.h"

#include "log.h"

int shock_parse_config(shock_config_t* conf, char* filename)
{
FILE *fp = fopen(filename, "r");
if (!fp){
shock_log(SHOCK_ERROR, "Cannot open config file for reading!");
printf("-------------------------------------------\n");
printf("[Error] Cannot open config file for reading!\n");
printf("-------------------------------------------\n");
Expand Down Expand Up @@ -40,6 +43,7 @@ int shock_parse_config(shock_config_t* conf, char* filename)
//If it is line ending, then let's set entry value
if (line[i] == '\n') {
if (phase == 0) {
shock_log(SHOCK_ERROR, "Unexpected ending of line %d in config!", lineNum);
printf("Config Error: unexpected ending of line %d\n", lineNum);
break;
}
Expand All @@ -55,6 +59,7 @@ int shock_parse_config(shock_config_t* conf, char* filename)
}

if (phase == 1) {
shock_log(SHOCK_ERROR, "Unexpected colon in value on line %d in config!", lineNum);
printf("Config Warning: unexpected colon in value on line %d\n", lineNum);
}

Expand All @@ -77,6 +82,7 @@ void shock_default_config(shock_config_t* conf)
conf->port = SHOCK_DEFAULT_PORT;
conf->clearLogs = SHOCK_DEAFULT_CLEARLOGS;
strcpy(conf->root, SHOCK_DEFAULT_ROOT);
strcpy(conf->logFile, SHOCK_DEFAULT_LOG);
}


Expand All @@ -89,6 +95,7 @@ int shock_parse_config_token(shock_config_t* conf, shock_config_keypair_t entry)
conf->port = strtol(entry.val, NULL, 10);
if (conf->port == 0) {
conf->port = SHOCK_DEFAULT_PORT;
shock_log(SHOCK_ERROR, "Invalid value for %s option in config!", entry.name);
printf("Config Error: Invalid value for %s option. \n", entry.name);
}
return;
Expand All @@ -106,6 +113,7 @@ int shock_parse_config_token(shock_config_t* conf, shock_config_keypair_t entry)
conf->clearLogs = 0;
} else {
printf("Config Error: Invalid value for %s option. \n", entry.name);
shock_log(SHOCK_ERROR, "Invalid value for %s option in config!", entry.name);
}
return;
}
Expand All @@ -117,9 +125,16 @@ int shock_parse_config_token(shock_config_t* conf, shock_config_keypair_t entry)
conf->etagCache = 0;
} else {
printf("Config Error: Invalid value for %s option. \n", entry.name);
shock_log(SHOCK_ERROR, "Invalid value for %s option in config!", entry.name);
}
return;
}

if (strcasecmp(entry.name, "LogFile") == 0) {
strncpy(conf->logFile, entry.val, sizeof(conf->logFile));
return;
}

shock_log(SHOCK_ERROR, "Unknown config option: %s", entry.name);
printf("Config Error: Unknown option %s\n", entry.name);
}
2 changes: 2 additions & 0 deletions config_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
#define SHOCK_DEFAULT_ROOT "htdocs"
#define SHOCK_DEAFULT_CLEARLOGS 0
#define SHOCK_DEAFULT_ETAGCACHE 1
#define SHOCK_DEFAULT_LOG "shockd.log"

typedef struct {
uint16_t port;
char root[FILENAME_MAX];
int clearLogs;
int etagCache;
char logFile[FILENAME_MAX];
} shock_config_t;

typedef struct {
Expand Down
75 changes: 75 additions & 0 deletions log.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include <stdio.h>
#include "config_file.h"
#include <stdarg.h>
#include "request.h"
#include "log.h"
#include "time.h"

#define MAX_LOGS 16

char* log_levels[] = {
"->",
"Info",
"Warning",
"Error",
};

FILE* logFile;
int logged = 0;

char* shock_format_time()
{
time_t t = time(NULL);
struct tm tm = *gmtime(&t);
char buf[128] = "";
sprintf(buf, "%d.%d.%d %d:%d:%d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
return strdup(buf);
}

int shock_log_init(shock_config_t* conf)
{
logFile = fopen (conf->logFile,"a");
if (!logFile) {
return -1;
}
return 0;
}

void shock_log(int level, char* format, ...)
{
logged++;
fprintf(logFile, "%s: [%s] ", shock_format_time(), log_levels[level]);
va_list args;
va_start (args, format);
vfprintf (logFile, format, args);
va_end (args);
fprintf(logFile, "\n"); //It also append newline character, because writing it in every call annoys
if (logged == MAX_LOGS) {
fflush(logFile);
logged = 0;
}
}

void shock_log_raw(char* format, ...)
{
va_list args;
va_start (args, format);
vfprintf (logFile, format, args);
va_end (args);
}



void shock_log_access(shock_request_t* req)
{
logged++;
fprintf(logFile, "%s: %s %s HTTP/1.%d (%s) \n", shock_format_time(), httpMethods[req->method], req->route, req->version, req->agent);
if (logged == MAX_LOGS) {
fflush(logFile);
logged = 0;
}
}




18 changes: 18 additions & 0 deletions log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef SHOCK_LOG_H
#define SHOCK_LOG_H

#include "config_file.h"
#include "request.h"

#define SHOCK_ACCESS 0
#define SHOCK_INFO 1
#define SHOCK_WARNING 2
#define SHOCK_ERROR 3

int shock_log_init(shock_config_t* conf);
void shock_log(int level, char* fmt, ...);
void shock_log_access(shock_request_t* req);
char* shock_format_time();
void shock_log_raw(char* format, ...);

#endif
18 changes: 16 additions & 2 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "base.h"
#include "cache.h"
#include "log.h"

shock_config_t conf;

Expand Down Expand Up @@ -54,6 +55,7 @@ void processRequest(SOCKET sock) {
return;
}

shock_log_access(&request);
//Now serve the file!
char* route = request.route;
char lastchar = route[strlen(route)-1];
Expand All @@ -64,7 +66,7 @@ void processRequest(SOCKET sock) {
sprintf(filename, "%s%s", conf.root, route);
if (conf.etagCache == 1 && request.etag[0] != 0) {
char *ptr;
time_t res = strtol(request.etag, &ptr, 10);
time_t res = strtoll(request.etag, &ptr, 10);
if (shock_cache_compare(filename, res) == 1) {
shock_response_notmodified(sock);
} else {
Expand Down Expand Up @@ -115,7 +117,12 @@ int main(int argc, char* argv[]) {
printf("[Config] Server Root: %s\n", conf.root);
printf("[Config] ClearLogs: %d\n", conf.clearLogs);
printf("[Config] ETag Cache: %d\n", conf.etagCache);
printf("[Config] Log File: %s\n", conf.logFile);

if (shock_log_init(&conf) == -1) {
printf("[Fatal Error] Cannot init logging system!\n");
return 1;
}

printf("\nResolving IP...\n\n");

Expand All @@ -125,6 +132,7 @@ int main(int argc, char* argv[]) {
server = socket(AF_INET , SOCK_STREAM , 0);
if (server == -1)
{
shock_log(SHOCK_ERROR, "Cannot create server socket!");
printf("[Fatal Error] Cannot create server socket!");
return 1;
}
Expand All @@ -136,6 +144,7 @@ int main(int argc, char* argv[]) {

if( bind(server,(struct sockaddr *)&serverIP , sizeof(serverIP)) < 0)
{
shock_log(SHOCK_ERROR, "Cannot bind socket to port!!");
printf("[Fatal Error] Cannot bind socket to port! \n");
return 1;
}
Expand All @@ -152,18 +161,23 @@ int main(int argc, char* argv[]) {
struct sockaddr_in clientIP;
size_t c = sizeof(struct sockaddr_in);

shock_log_raw("-----------------------------------------------\n");
shock_log_raw("NEW SERVER SESSION STARTED AT %s\n", shock_format_time());
shock_log_raw("-----------------------------------------------\n");
shock_log(SHOCK_INFO, "Server started!");
printf("[!] Done. Waiting for incoming connections...\n");
while (1) {
client=accept(server, (struct sockaddr *)&clientIP, (socklen_t*)&c);
if(client < 0)
{
shock_log(SHOCK_ERROR, "Couldn't accept incoming connection!");
printf("[Connection Error] Failed to accept incoming connection!");
continue;
}

shock_create_thread(processRequest, client);
}

printf("--- END ---");
printf("--- END, THAT SHOULDN'T BE REACHED ---");
return 0;
}
5 changes: 5 additions & 0 deletions request.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

#define HTTP_HEADER(x) char x[HEADER_MAX]

static char* httpMethods[] = {
"GET",
"POST"
};

typedef struct {
int version; //Version of HTTP protocol (0 means HTTP/1.0, 1 means HTTP/1.1)
int method; //HTTP method
Expand Down
2 changes: 2 additions & 0 deletions response.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <time.h>

#include "base.h"
#include "log.h"

char* shock_date_now() {
char buf[256];
Expand Down Expand Up @@ -41,6 +42,7 @@ void shock_serve_file(SOCKET sock, char* filename)
buflen = fread(buf, 1, sizeof(buf), fp);
if (buflen < 1) {
if (!feof(fp)) {
shock_log(SHOCK_ERROR, "Failed to read %s file", filename);
printf("Internal Server Error: An error occured while reading file: %s \n", filename);
shock_error_internal(sock, "Failed to read data from requested file!");
break;
Expand Down
2 changes: 1 addition & 1 deletion server_defines.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef SHOCK_DEFINES_H
#define SHOCK_DEFINES_H

#define SERVER_STRING "ShockDaemon/0.9.6"
#define SERVER_STRING "ShockDaemon/1.0.0"
#define ERROR_DELAY 2500
#define REQUEST_MAX 3072
#define ROUTE_MAX 1024
Expand Down
3 changes: 3 additions & 0 deletions shockd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ ClearLogs: 0
# Enables ETag caching (Boolean, Default: 1)
ETagCache: 1

# Path to log file (String, Default: shockd.log)
LogFile: shockd.log

# Please, always leave newline character (\n) at the end of file to avoid undefined behaviour
# Also, lines should NOT be longer than 512 symbols in total.
# -- END OF CONFIG --
Empty file added shockd.log
Empty file.

0 comments on commit 01f9f7a

Please sign in to comment.