酷呆了。虽然功能基本,但是已经做基本工作了。
转自:http://blog.sina.com.cn/s/blog_7530db6f0100wh5j.html
为了更好的了解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);
}