为了更好的了解HTTP协议, 特意谢了一个简单HTTP服务器, 代码只有400行. 因为很简单, 所以效率也不怎么高, 而且支持的特性也不多, 不过也可以运行, 性能跟Apache差不多.



=============================================================================================

#include <fcntl.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <netinet/tcp.h>

#include <errno.h>

#include <sys/ioctl.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define BUFFER_INIT_SIZE 512

enum {

    mickey_reading_header_stage,

    mickey_writing_header_stage,

    mickey_writing_body_stage,

    mickey_closing_stage

};

enum {

    mickey_http_ok,

    mickey_http_notfound,

    mickey_http_error

};

enum {

    mickey_method_get,

    mickey_method_head,

    mickey_method_unknow

};

typedef struct {

    char *buff;

    int size;

    int free;

} mickey_buffer_t;

typedef struct {

    int sock;

    mickey_buffer_t *request;

    mickey_buffer_t *response;

    int keepalive;

    int method;

    mickey_buffer_t *uri;

    int status;

    int stage;

    FILE *handle;

} mickey_connection_t;

static int srv_sock;

mickey_buffer_t *mickey_buffer_new() {

    mickey_buffer_t *object;

   

    object = malloc(sizeof(*object));

    if (object) {

        object->buff = malloc(BUFFER_INIT_SIZE + 1);

        if (!object->buff) {

            free(object);

            return NULL;

        }

        object->size = BUFFER_INIT_SIZE;

        object->free = BUFFER_INIT_SIZE;

    }

    return object;

}

int mickey_buffer_append_length(mickey_buffer_t *buf, void*data, int length) {

    int lack, need = 0;

    char *temp;

   

    if (length > buf->free) {

        lack = length - buf->free;

        while (need < lack)

            need += BUFFER_INIT_SIZE;

        temp = realloc(buf->buff, buf->size + need + 1);

        if (!temp)

            return -1;

        buf->buff = temp;

        buf->size += need;

        buf->free += need;

    }

    memcpy(buf->buff + (buf->size - buf->free), data, length);

    buf->free -= length;

    buf->buff[buf->size - buf->free] = '\0';

    return 0;

}

int mickey_buffer_append(mickey_buffer_t *buf, void *data){

    return mickey_buffer_append_length(buf, data, strlen((char *)data));

}

int mickey_buffer_find_string(mickey_buffer_t *buf, char *str){

    int idx = buf->size - buf->free;

    int slen = strlen(str);

    int i;

    for (i = 0; i < idx; i++) {

        if (idx - i >= slen) {

            if (!memcmp(buf->buff + i, str, slen)) return 1;

        } else {

            break;

        }

    }

    return 0;

}

int mickey_buffer_length(mickey_buffer_t *buf) {

    return buf->size - buf->free;

}

void mickey_buffer_print(mickey_buffer_t *buf) {

    fprintf(stderr, "%s", buf->buff);

}

void mickey_buffer_clean(mickey_buffer_t *buf) {

    buf->free = buf->size;

}

void mickey_buffer_free(mickey_buffer_t *buf) {

    if (!buf)

        return;

    if (buf->buff)

        free(buf->buff);

    free(buf);

}

int mickey_header_finish(mickey_connection_t *conn) {

    int end = conn->request->size - conn->request->free;

    if (conn->request->buff[end - 1] == '\n' &&

        conn->request->buff[end - 2] == '\r' &&

        conn->request->buff[end - 3] == '\n' &&

        conn->request->buff[end - 4] == '\r')

        return 1;

    return 0;

}

void mickey_parse_header(mickey_connection_t *conn) {

    char *eol;

    char method[16], uri[256], protocol[32];

   

    eol = strchr(conn->request->buff, '\n');

    if (eol == NULL) {

        conn->stage = mickey_closing_stage;

        return;

    }

   

    if (*(eol-1) == '\r')

        *(eol-1) = '\0';

    *eol = '\0';

    sscanf(conn->request->buff, "s %6s 2s", method, uri, protocol);

    if (!strcmp(method, "GET")) {

        conn->method = mickey_method_get;

    } else if (!strcmp(method, "HEAD")) {

        conn->method = mickey_method_head;

    } else {

        conn->method = mickey_method_unknow;

    }

    mickey_buffer_append(conn->uri, ".");

    mickey_buffer_append(conn->uri, uri);

    if (mickey_buffer_find_string(conn->uri, "..")) {

        fprintf(stderr, "[x] mickey found connection header exists (..)\n");

        conn->stage = mickey_closing_stage;

    }

}

void connection_reading_header(mickey_connection_t *conn){

    char buff[1024];

    int nrecv;

   

    nrecv = read(conn->sock, buff, 1024);

    if (nrecv > 0) {

        mickey_buffer_append_length(conn->request, buff, nrecv);

        if (mickey_header_finish(conn)) {

            mickey_parse_header(conn);

            conn->stage = mickey_writing_header_stage;

        }

    } else {

        fprintf(stderr, "[x] mickey cannot read data from connection.\n");

        conn->stage = mickey_closing_stage;

    }

}

void connection_make_header(mickey_connection_t *conn) {

    struct stat stat_buf;

   

    if (stat(conn->uri->buff, &stat_buf) == -1) {

        conn->status = mickey_http_notfound;

        mickey_buffer_append(conn->response, "HTTP/1.0 404 Not Found\r\n");

    } else {

        if (S_ISDIR(stat_buf.st_mode)) {

            mickey_buffer_append(conn->uri, "index.html");

            if (stat(conn->uri->buff, &stat_buf) == -1) {

                conn->status = mickey_http_notfound;

                mickey_buffer_append(conn->response, "HTTP/1.0 404 Not Found\r\n");

            } else {

                char content_length[256];

               

                conn->handle = fopen(conn->uri->buff, "r");

                if (!conn->handle) {

                    mickey_buffer_append(conn->response, "HTTP/1.0 500 Internal Server Error\r\n");

                } else {

                    mickey_buffer_append(conn->response, "HTTP/1.0 200 OK\r\n");

                    sprintf(content_length, "Content-Length: %d\r\n", stat_buf.st_size);

                    mickey_buffer_append(conn->response, content_length);

                }

            }

        } else if (S_ISREG(stat_buf.st_mode)) {

            char content_length[256];

           

            conn->handle = fopen(conn->uri->buff, "r");

            if (!conn->handle) {

                mickey_buffer_append(conn->response, "HTTP/1.0 500 Internal Server Error\r\n");

            } else {

                mickey_buffer_append(conn->response, "HTTP/1.0 200 OK\r\n");

                sprintf(content_length, "Content-Length: %d\r\n", stat_buf.st_size);

                mickey_buffer_append(conn->response, content_length);

            }

        } else {

            mickey_buffer_append(conn->response, "HTTP/1.0 500 Internal Server Error\r\n");

        }

    }

    mickey_buffer_append(conn->response, "\r\n");

    //mickey_buffer_print(conn->response);

}

void connection_send_header(mickey_connection_t *conn) {

    int bytes = mickey_buffer_length(conn->response);

    int nsend, i = 0;

   

    while (i < bytes) {

        nsend = write(conn->sock, conn->response->buff + i, bytes - i);

        if (nsend > 0) {

            i += nsend;

        } else {

            sleep(1);

            continue;

        }

    }

    conn->stage = mickey_writing_body_stage;

}

void connection_send_body(mickey_connection_t *conn) {

    char buff[1024];

    int bytes;

   

    if (!conn->handle) {

        conn->stage = mickey_closing_stage;

        return;

    }

   

    while (!feof(conn->handle)) {

        int ws = 0;

        int hs = 0;

       

        fread(buff, 1024, 1, conn->handle);

        bytes = strlen(buff);

        while (ws < bytes) {

            hs = write(conn->sock, buff + ws, bytes - ws);

            if (hs > 0) {

                ws += hs;

            } else {

                sleep(1);

                continue;

            }

        }

    }

    fclose(conn->handle);

    conn->stage = mickey_closing_stage;

}

mickey_connection_t *connection_new(int sock) {

    mickey_connection_t *conn;

   

    conn = malloc(sizeof(*conn));

    if (conn) {

        conn->sock      = sock;

        conn->keepalive = 0;

        conn->stage     = mickey_reading_header_stage;

        conn->status    = mickey_http_ok;

        conn->request   = mickey_buffer_new();

        conn->response  = mickey_buffer_new();

        conn->uri       = mickey_buffer_new();

        if (!conn->request || !conn->response || !conn->uri) {

            mickey_buffer_free(conn->request);

            mickey_buffer_free(conn->response);

            mickey_buffer_free(conn->uri);

            free(conn);

            return NULL;

        }

        return conn;

    }

   

    return NULL;

}

void connection_exit(mickey_connection_t *conn) {

    if (conn->request)

        mickey_buffer_free(conn->request);

    if (conn->response)

        mickey_buffer_free(conn->response);

    if (conn->uri)

        mickey_buffer_free(conn->uri);

    free(conn);

}

void *connection_thread_entrance(void *arg) {

    mickey_connection_t *conn = (mickey_connection_t *)arg;

   

    pthread_detach(pthread_self());

   

    fprintf(stderr, "[+] === mickey thread working ===\n");

   

    while (1) {

        switch (conn->stage) {

        case mickey_reading_header_stage:

            connection_reading_header(conn);

            break;

        case mickey_writing_header_stage:

            connection_make_header(conn);

            connection_send_header(conn);

            break;

        case mickey_writing_body_stage:

            connection_send_body(conn);

            break;

        case mickey_closing_stage:

            close(conn->sock);

            connection_exit(conn);

            goto THREAD_CLOSING;

            break;

        }

    }

THREAD_CLOSING:

    fprintf(stderr, "[-] === mickey thread closing ===\n");

    pthread_exit(NULL);

}

int initialize_server() {

    struct sockaddr_in addr;

    struct linger ling = {0, 0};

    int flags = 1;

   

    srv_sock = socket(AF_INET, SOCK_STREAM, 0);

    if (srv_sock == -1)

        return -1;

    setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));

    setsockopt(srv_sock, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));

    setsockopt(srv_sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));

#if !defined(TCP_NOPUSH)

    setsockopt(srv_sock, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags));

#endif

   

    addr.sin_family = AF_INET;

    addr.sin_port = htons(8080);

    addr.sin_addr.s_addr = htonl(INADDR_ANY);

   

    if (bind(srv_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1)

        return -1;

    if (listen(srv_sock, 1024) == -1)

        return -1;

    return srv_sock;

}

void socket_accept_connection() {

    int new_sock;

    socklen_t len;

    struct sockaddr addr;

    mickey_connection_t *new_conn;

    pthread_t tid;

   

    while (1) {

        new_sock = accept(srv_sock, &addr, &len);

        if (new_sock == -1) {

            if (errno != EAGAIN && errno != EWOULDBLOCK) {

                continue;

            }

            goto ERROR_FLAG;

        }

        new_conn = connection_new(new_sock);

        if (!new_conn) {

            fprintf(stderr, "[x] mickey not enough momery.\n");

            close(new_sock);

            goto ERROR_FLAG;

        }

        pthread_create(&tid, NULL, connection_thread_entrance, new_conn);

    }

   

ERROR_FLAG:

    fprintf(stderr, "[x] mickey cannot accept client connection.\n");

    return;

}

int main(int argc, char **argv) {

    chdir("./");

    if (initialize_server() == -1) {

        fprintf(stderr, "[x] mickey initialize failed.\n");

        exit(1);

    }

    socket_accept_connection();

    exit(0);

}

 

分享:

 



0

一个简单的HTTP服务器(多线程)_#if喜欢


0

一个简单的HTTP服务器(多线程)_#if赠金笔