Skip to main content
  1. Posts/

Simple Web Server with C programming

·559 words

Introduction #

This course is to implement simple static web server using C (multi-processing)

header files #

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define HEADER_FMT "HTTP/1.1 %d %s\nContent-Length: %ld\nContent-Type: %s\n\n"
#define NOT_FOUND_CONTENT       "<h2>404 Not Found</h2>\n"
#define SERVER_ERROR_CONTENT    "<h2>500 Internal Server Error</h2>\n"

#define BUF_SIZE 1000

main #

Detail information about socket functions

int main(int argc, char **argv) {
 int port;
 int lsock, asock;

 struct sockaddr_in remote_sin;
 socklen_t remote_sin_len;

 // can specify your own port number
 port = 3000;

 // create listen socket using socket function
 lsocket = socket(AF_INET, SOCK_STREAM, 0);
    
 // lsock == -1 if failed creating socket
 if (lsock < 0) {
  perror("[ERR] failed to create lsock.\n");
 }
 if (bind_lsock(lsock, port) < 0) {
  perror("[ERR] failed to listen lsock.\n");
    exit(1);
 }
 if (listen(lsock, 10) < 0) {
    perror("[ERR] failed to listen lsock.\n");
    exit(1);
  }

 // to prevent zombie process
 signal(SIGCHLD, SIG_IGN);

 int pid;
 while(1) {
  // accept request from client
  asock = accept(lsock, (struct sockaddr *)&remote_sin,
   &remote_sin_len);
  if (asock < 0) {
   perror("[ERR] failed to accept.\n");
   exit(1);
  }

  // multi-processing
  pid = fork();
  
  if (pid == 0) {
   // handling client socket and close sockets
   close(lsock); http_handler(asock); close(asock);
   exit(0);
  }

  if (pid != 0) { close(asock); }
  if (pid < 0){ perror("[ERR] failed to fork.\n"); }
 }
}

http_handler #

handler that provides index.html for empty uri (” / ”) handle_400, handle_500, find_mime, fill_header function will be explained at the next block

void http_handler(int asock) {
 char buf[BUF_SIZE];

 if (read(asock, buf, BUF_SIZE) < 0) {
  perror("[ERR] Failed to read request.\n");
    handle_500(asock);
 }

 char *method = strtok(buf, " ");
 char *uri = strtok(NULL, " ");
 if (method == NULL || uri == NULL) {
  perror("[ERR] Failed to identify method, URI.\n");
    handle_500(asock); return;
 }

 char safe_uri[BUF_SIZE];
 char *local_uri;
 struct stat st;

 strcpy(safe_uri, uri);
 // strcmp returns 0 if two strings are equal
 if (!strcmp(safe_uri, "/")) {
  strcpy(safe_uri, "./index.html");
 }
 
 // local_uri = "index.html"
 local_uri = safe_uri + 1;
 if (stat(safe_uri, &st) < 0) {
  perror("[WARN] No File found matching URI.\n");
  handle_404(asock); return;
 }

 int fd = open(safe_uri, O_RDONLY);
  if (fd < 0) {
      perror("[ERR] Failed to open file.\n");
      handle_500(asock); return;
  }

 char header[BUF_SIZE];

 // content_length
 int ct_len = st.st_size;
 char content_type[40];
 find_mime(content_type, local_uri);
 fill_header(header, 200, ct_len, content_type);
 write(asock, header, strlen(header));
 
 int content;
 while ((content = read(fd, buf, BUF_SIZE)) > 0) {
  write(asock, buf, content);
 }
}

fill_header #

void fill_header(char *header, int status, long len, char *type) {
 char status_text[40];
 switch (status) {
  case 200:
   strcpy(status_text, "OK"); break;
  case 404:
   strcpy(status_text, "NOT FOUND"); break;
  case 500:
  default:
   strcpy(status_text, "INTERNAL SERVER ERROR"); break;
 }
 // fill header using HEADER_FORMAT which is defined with header files
 sprintf(header, HEADER_FMT, status, status_text, len, type);
}

find_mime #

function that find MIME type (media type) of contents

void find_mime(char *content_type, char *uri) {
    // strrchr = locate last character and return pointer
    char *ext = strrchr(uri, '.');
    if (!strcmp(ext, ".html")) {
        strcpy(content_type, "text/html");
    } else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) {
        strcpy(content_type, "image/jpeg");
    } else if (!strcmp(ext, ".png")) {
        strcpy(content_type, "image/png");
    } else if (!strcmp(ext, ".css")) {
        strcpy(content_type, "text/css");
    } else if (!strcmp(ext, ".js")) {
        strcpy(content_type, "text/javascript");
    } else strcpy(content_type, "text/plain");
}

handle_404, handle_500 #

void handle_404(int asock) {
    char header[BUF_SIZE];
    fill_header(header, 404, sizeof(NOT_FOUND_CONTENT), "text/html");

    write(asock, header, strlen(header));
    write(asock, NOT_FOUND_CONTENT, strlen(header));
}

void handle_500(int asock) {
    char header[BUF_SIZE];
    fill_header(header, 500, sizeof(SERVER_ERROR_CONTENT), "text/html");

    write(asock, header, strlen(header));
    write(asock, SERVER_ERROR_CONTENT, strlen(header));
}