Skip to content

Instantly share code, notes, and snippets.

@flouthoc
Created February 20, 2019 17:33
Show Gist options
  • Save flouthoc/98658f68b58f6ad236c4ee88727ac7aa to your computer and use it in GitHub Desktop.
Save flouthoc/98658f68b58f6ad236c4ee88727ac7aa to your computer and use it in GitHub Desktop.
Please don't look
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
/*Reading conventions
MACROS - all the tokens which are in full capital letters.
ENUMS - all the tokens which start with capital letter and then switch to small.
STRUCTS - all small letters most of the time they will contain underscore
VARS - all small you'll notice them :)
*/
/*HTTP versions*/
#define VERSION_11 "1.1"
#define VERSION_10 "1.0"
#define MAXEVENTS 64
/*Header Status*/
char global_buffer[1024];
enum Method{ /*only as much as method server needs to process*/
GET, POST
};
enum Http_status{
HTTP_STATUS_OK = 200,
HTTP_STATUS_CREATED = 201,
HTTP_STATUS_ACCEPTED = 202,
HTTP_STATUS_BAD_REQUEST = 400,
HTTP_STATUS_NOT_FOUND = 404,
};
struct http_request{ /*header only as much as we need*/
char version[4];
enum Method method;
char *uri;
};
struct http_response{ /*this is response body which we will use through out the code*/
char *header;
size_t length;
int is_content; //this will be 1 if there is any message body in this response and 0 if not , this is just to add extra rn when replying
};
void print_rn(char * str)
{
while ( *str ) {
if ( *str == '\n' ) {
fputs("\n", stdout);
} else if ( *str == '\r' ) {
fputs("\r", stdout);
} else {
fputc(*str, stdout);
}
str++;
}
fputs("\n", stdout);
}
static inline void extend_header(struct http_response *handle,const char *extension, size_t length)
{
struct http_response *h = handle;
h->length += length;
h->header = realloc(h->header, h->length);
strcat(h->header, extension);
}
static void reply(struct http_response *handle, int connectionfd){
int written, n;
char *buf;
if(handle->is_content == 0) extend_header(handle, "\r\n", 2);
n = handle->length;
buf = handle->header;
print_rn(buf);
while(n > 0){
if( (written = write(connectionfd, buf, (handle->length+1))) > 0){
buf += written;
n -= written;
}else{
perror("When replying to file descriptor");
}
}
free(handle->header);
free(handle);
}
static inline void prepare_status_line(struct http_response **response_handle, char *version, enum Http_status status){
struct http_response *r = malloc(sizeof(struct http_response));
/*Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF*/
/*I wont use Reason-Phrase cause its not important for me now*/
r->header = malloc(16*sizeof(char)); //hard-coding length for HTTP/X.X(8) SP(1) STATUS-CODE(3) SP(1) CRLF(2)
r->length = sprintf(r->header, "HTTP/%s %d \r\n", version, status);
r->is_content = 0;
*response_handle = r;
}
void setHeader(struct http_response *handle, char *header, char*value){
size_t len;
char local_buffer[1024];
strcpy(local_buffer, value);
len = sprintf(global_buffer, "%s: %s\r\n", header, local_buffer);
extend_header(handle, global_buffer, len);
}
void replywithContent(struct http_response *response_handle, int connectionfd, const char *content){
size_t content_length = strlen(content);
sprintf(global_buffer, "%d", (int)content_length);
setHeader(response_handle, "Content-Length", global_buffer);
setHeader(response_handle, "Content-Type", "text/html");
extend_header(response_handle, "\r\n", 2);
response_handle->is_content = 1;
extend_header(response_handle, content, content_length);
reply(response_handle, connectionfd);
}
void router(struct http_request *request_handle, int connectionfd, struct http_response **response_handle){
if(!strcmp(request_handle->uri, "/hello")){
prepare_status_line(response_handle, request_handle->version, HTTP_STATUS_OK);
replywithContent(*response_handle, connectionfd, "Hello");
}else if(!strcmp(request_handle->uri, "/hey")){
prepare_status_line(response_handle, request_handle->version, HTTP_STATUS_OK);
replywithContent(*response_handle, connectionfd, "Hey hi this is a test");
}else{
prepare_status_line(response_handle, request_handle->version, HTTP_STATUS_NOT_FOUND);
replywithContent(*response_handle, connectionfd, "Not Found");
}
}
static int parse_http_request(struct http_request **req, char *request_buffer) /*parsing only as much as we need to make this server work*/
{
int length;
char *uri;
char *token_handler;
struct http_request *p = (struct http_request *)(malloc(sizeof(struct http_request)));
token_handler = strtok(request_buffer, " ");
if(!strcmp(token_handler, "GET")) p->method = GET;
else if(!strcmp(token_handler, "POST")) p->method = POST;
else return 0;
token_handler = strtok(NULL, " ");
length = strlen(token_handler);
uri = malloc(sizeof(char) * (length + 1));
strcpy(uri, token_handler);
p->uri = uri;
token_handler = strtok(NULL, " ");
if(!strncmp(token_handler, "HTTP/1.1\r\n", 10)) strcpy(p->version, VERSION_11);
else if(!strncmp(token_handler, "HTTP/1.1\r\n", 10)) strcpy(p->version, VERSION_10);
else return 0;
*req = p;
return 1;
}
void respond(struct http_request *request_handle, int connectionfd)
{
struct http_response *response_handle;
if(request_handle->method == GET) router(request_handle, connectionfd, &response_handle);
else goto error;
return;
error:
prepare_status_line(&response_handle, request_handle->version, HTTP_STATUS_BAD_REQUEST);
reply(response_handle, connectionfd);
}
int main(){
int n,i;
int listenfd;
int listenfd_flags;
int epoll_fd;
char msg_buffer[1024];
char temp_buffer[1024];
int connectionfd;
int client_length;
struct http_request *request_handle;
struct sockaddr_in server_address, client_address;
struct epoll_event event;
struct epoll_event *events;
char greet[] = "Heelo";
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd == -1){
perror("Socket");
exit(1);
}
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("192.168.1.6");
server_address.sin_port = htons(3000);
if(bind(listenfd, (struct sockaddr*)&server_address, sizeof(server_address)) == -1){
perror("Bind");
exit(1);
}
if((listenfd_flags = fcntl(listenfd, F_GETFL, 0)) == -1){
perror("While trying to get listenfd flags");
exit(1);
}
listenfd_flags |= O_NONBLOCK;
if(fcntl(listenfd, F_SETFL, listenfd_flags) == -1){
perror("While trying to set flags");
exit(1);
}
if(listen(listenfd, 10) == -1){
perror("Listen");
exit(1);
}
if((epoll_fd = epoll_create1(0)) == -1){
perror("epoll_create");
exit(1);
}
event.data.fd = listenfd;
event.events = EPOLLIN;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenfd, &event) == -1){
perror("Epoll context");
exit(1);
}
events = calloc(MAXEVENTS, sizeof event);
/*evet-loop*/
while (1){
n = epoll_wait (epoll_fd, events, MAXEVENTS, -1);
for (i = 0; i < n; i++){
if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) ||(!(events[i].events & EPOLLIN))){
close (events[i].data.fd);
continue;
}
else if (listenfd == events[i].data.fd){
while (1){
int connectionfd_flags;
connectionfd = accept(listenfd, (struct sockaddr*)&client_address, &client_length);
if (connectionfd == -1){
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
{
/* We have processed all incoming
connections. */
break;
}
else
{
perror ("accept");
break;
}
}
/* We musk make client's socket NON-BLOCKING */
if((connectionfd_flags = fcntl(connectionfd, F_GETFL, 0)) == -1){
perror("While trying to get listenfd flags");
exit(1);
}
connectionfd_flags |= O_NONBLOCK;
if(fcntl(connectionfd, F_SETFL, connectionfd_flags) == -1){
perror("While trying to set flags");
exit(1);
}
event.data.fd = connectionfd;
event.events = EPOLLIN;
if(epoll_ctl (epoll_fd, EPOLL_CTL_ADD, connectionfd, &event) == -1){
perror ("epoll_ctl");
exit(1);
}
}
continue;
}
else
{
memset(msg_buffer, 0, sizeof(msg_buffer));
memset(temp_buffer, 0, sizeof(temp_buffer));
while ((n = read(events[i].data.fd, msg_buffer, 1024))){
if (n == -1){
// printf("Might be EAGAIN\n");
break;
}
}
strcpy(temp_buffer, msg_buffer);
if(!parse_http_request(&request_handle, temp_buffer)){
printf("Request Unparsablen\n");
close(events[i].data.fd);
continue;
}
respond(request_handle, events[i].data.fd);
close(events[i].data.fd);
free(request_handle->uri);
free(request_handle);
//printf("%s\n", temp_buffer);
}
}
}
/*
//This was some rough loop so i'm commenting it
while(1){
client_length = sizeof(client_address);
memset(msg_buffer, 0, sizeof(msg_buffer));
memset(temp_buffer, 0, sizeof(temp_buffer));
connectionfd = accept(listenfd, (struct sockaddr*)&client_address, &client_length);
while( n = recv(connectionfd, msg_buffer, 1024, MSG_DONTWAIT)){
//printf("%sn", msg_buffer);
//printf("%zdn", strlen(msg_buffer));
if(strstr(msg_buffer, "rnrn"))
break;
//if( n < 0)
// if(errno == EWOULDBLOCK)
// break;
}
strcpy(temp_buffer, msg_buffer);
if(!parse_http_request(&request_handle, temp_buffer)){
printf("Request Unparsablen");
close(connectionfd);
continue;
}
respond(request_handle, connectionfd);
close(connectionfd);
free(request_handle->uri);
free(request_handle);
}*/
exit(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment