Skip to content

Instantly share code, notes, and snippets.

@cristian-programmer
Forked from laobubu/ABOUT.md
Created July 22, 2019 19:06
Show Gist options
  • Save cristian-programmer/1a24cdba34dfd3b5b4103a48a4ec0662 to your computer and use it in GitHub Desktop.
Save cristian-programmer/1a24cdba34dfd3b5b4103a48a4ec0662 to your computer and use it in GitHub Desktop.

Revisions

  1. @laobubu laobubu revised this gist Nov 8, 2016. 2 changed files with 23 additions and 11 deletions.
    31 changes: 21 additions & 10 deletions httpd.c
    Original file line number Diff line number Diff line change
    @@ -1,15 +1,16 @@
    #include "httpd.h"

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

    #define CONNMAX 1000

    @@ -42,6 +43,9 @@ void serve_forever(const char *PORT)
    for (i=0; i<CONNMAX; i++)
    clients[i]=-1;
    startServer(PORT);

    // Ignore SIGCHLD to avoid zombie threads
    signal(SIGCHLD,SIG_IGN);

    // ACCEPT connections
    while (1)
    @@ -140,6 +144,13 @@ void respond(int n)
    prot = strtok(NULL, " \t\r\n");

    fprintf(stderr, "\x1b[32m + [%s] %s\x1b[0m\n", method, uri);

    if (qs = strchr(uri, '?'))
    {
    *qs++ = '\0'; //split URI
    } else {
    qs = uri - 1; //use an empty string
    }

    header_t *h = reqhdr;
    char *t, *t2;
    3 changes: 2 additions & 1 deletion httpd.h
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,8 @@ void serve_forever(const char *PORT);
    // Client request

    char *method, // "GET" or "POST"
    *uri, // "/index.html"
    *uri, // "/index.html" things before '?'
    *qs, // "a=1&b=2" things after '?'
    *prot; // "HTTP/1.1"

    char *payload; // for POST
  2. @laobubu laobubu created this gist Nov 7, 2016.
    17 changes: 17 additions & 0 deletions ABOUT.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    # Pico HTTP Server in C

    This is a very simple HTTP server for Unix, using fork(). It's very easy to use

    ## How to use

    1. include header `httpd.h`
    2. write your route method, handling requests.
    3. call `serve_forever("12913")` to start serving on port 12913

    See `main.c`, an interesting example.

    To log stuff, use `fprintf(stderr, "message");`

    View `httpd.h` for more information

    based on <http://blog.abhijeetr.com/2010/04/very-simple-http-server-writen-in-c.html>
    15 changes: 15 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    all: server

    clean:
    @rm -rf *.o
    @rm -rf server

    server: main.o httpd.o
    gcc -o server $^

    main.o: main.c httpd.h
    gcc -c -o main.o main.c

    httpd.o: httpd.c httpd.h
    gcc -c -o httpd.o httpd.c

    180 changes: 180 additions & 0 deletions httpd.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,180 @@
    #include "httpd.h"

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

    #define CONNMAX 1000

    static int listenfd, clients[CONNMAX];
    static void error(char *);
    static void startServer(const char *);
    static void respond(int);

    typedef struct { char *name, *value; } header_t;
    static header_t reqhdr[17] = { {"\0", "\0"} };
    static int clientfd;

    static char *buf;

    void serve_forever(const char *PORT)
    {
    struct sockaddr_in clientaddr;
    socklen_t addrlen;
    char c;

    int slot=0;

    printf(
    "Server started %shttp://127.0.0.1:%s%s\n",
    "\033[92m",PORT,"\033[0m"
    );

    // Setting all elements to -1: signifies there is no client connected
    int i;
    for (i=0; i<CONNMAX; i++)
    clients[i]=-1;
    startServer(PORT);

    // ACCEPT connections
    while (1)
    {
    addrlen = sizeof(clientaddr);
    clients[slot] = accept (listenfd, (struct sockaddr *) &clientaddr, &addrlen);

    if (clients[slot]<0)
    {
    perror("accept() error");
    }
    else
    {
    if ( fork()==0 )
    {
    respond(slot);
    exit(0);
    }
    }

    while (clients[slot]!=-1) slot = (slot+1)%CONNMAX;
    }
    }

    //start server
    void startServer(const char *port)
    {
    struct addrinfo hints, *res, *p;

    // getaddrinfo for host
    memset (&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    if (getaddrinfo( NULL, port, &hints, &res) != 0)
    {
    perror ("getaddrinfo() error");
    exit(1);
    }
    // socket and bind
    for (p = res; p!=NULL; p=p->ai_next)
    {
    int option = 1;
    listenfd = socket (p->ai_family, p->ai_socktype, 0);
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
    if (listenfd == -1) continue;
    if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) break;
    }
    if (p==NULL)
    {
    perror ("socket() or bind()");
    exit(1);
    }

    freeaddrinfo(res);

    // listen for incoming connections
    if ( listen (listenfd, 1000000) != 0 )
    {
    perror("listen() error");
    exit(1);
    }
    }


    // get request header
    char *request_header(const char* name)
    {
    header_t *h = reqhdr;
    while(h->name) {
    if (strcmp(h->name, name) == 0) return h->value;
    h++;
    }
    return NULL;
    }

    //client connection
    void respond(int n)
    {
    int rcvd, fd, bytes_read;
    char *ptr;

    buf = malloc(65535);
    rcvd=recv(clients[n], buf, 65535, 0);

    if (rcvd<0) // receive error
    fprintf(stderr,("recv() error\n"));
    else if (rcvd==0) // receive socket closed
    fprintf(stderr,"Client disconnected upexpectedly.\n");
    else // message received
    {
    buf[rcvd] = '\0';

    method = strtok(buf, " \t\r\n");
    uri = strtok(NULL, " \t");
    prot = strtok(NULL, " \t\r\n");

    fprintf(stderr, "\x1b[32m + [%s] %s\x1b[0m\n", method, uri);

    header_t *h = reqhdr;
    char *t, *t2;
    while(h < reqhdr+16) {
    char *k,*v,*t;
    k = strtok(NULL, "\r\n: \t"); if (!k) break;
    v = strtok(NULL, "\r\n"); while(*v && *v==' ') v++;
    h->name = k;
    h->value = v;
    h++;
    fprintf(stderr, "[H] %s: %s\n", k, v);
    t = v + 1 + strlen(v);
    if (t[1] == '\r' && t[2] == '\n') break;
    }
    t++; // now the *t shall be the beginning of user payload
    t2 = request_header("Content-Length"); // and the related header if there is
    payload = t;
    payload_size = t2 ? atol(t2) : (rcvd-(t-buf));

    // bind clientfd to stdout, making it easier to write
    clientfd = clients[n];
    dup2(clientfd, STDOUT_FILENO);
    close(clientfd);

    // call router
    route();

    // tidy up
    fflush(stdout);
    shutdown(STDOUT_FILENO, SHUT_WR);
    close(STDOUT_FILENO);
    }

    //Closing SOCKET
    shutdown(clientfd, SHUT_RDWR); //All further send and recieve operations are DISABLED...
    close(clientfd);
    clients[n]=-1;
    }
    36 changes: 36 additions & 0 deletions httpd.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    #ifndef _HTTPD_H___
    #define _HTTPD_H___

    #include <string.h>
    #include <stdio.h>

    //Server control functions

    void serve_forever(const char *PORT);

    // Client request

    char *method, // "GET" or "POST"
    *uri, // "/index.html"
    *prot; // "HTTP/1.1"

    char *payload; // for POST
    int payload_size;

    char *request_header(const char* name);

    // user shall implement this function

    void route();

    // some interesting macro for `route()`
    #define ROUTE_START() if (0) {
    #define ROUTE(METHOD,URI) } else if (strcmp(URI,uri)==0&&strcmp(METHOD,method)==0) {
    #define ROUTE_GET(URI) ROUTE("GET", URI)
    #define ROUTE_POST(URI) ROUTE("POST", URI)
    #define ROUTE_END() } else printf(\
    "HTTP/1.1 500 Not Handled\r\n\r\n" \
    "The server has no handler to the request.\r\n" \
    );

    #endif
    27 changes: 27 additions & 0 deletions main.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    #include "httpd.h"

    int main(int c, char** v)
    {
    serve_forever("12913");
    return 0;
    }

    void route()
    {
    ROUTE_START()

    ROUTE_GET("/")
    {
    printf("HTTP/1.1 200 OK\r\n\r\n");
    printf("Hello! You are using %s", request_header("User-Agent"));
    }

    ROUTE_POST("/")
    {
    printf("HTTP/1.1 200 OK\r\n\r\n");
    printf("Wow, seems that you POSTed %d bytes. \r\n", payload_size);
    printf("Fetch the data using `payload` variable.");
    }

    ROUTE_END()
    }