diff --git a/simplehttpd.c b/simplehttpd.c new file mode 100644 index 0000000000000000000000000000000000000000..74b1a34039fc7f2838e93b46dce87fa3975df8ea --- /dev/null +++ b/simplehttpd.c @@ -0,0 +1,523 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "simplehttpd.h" + +#define ISspace(x) isspace((int)(x)) + +#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n" + +typedef void (*PostProcessDoneCallback)(int, const char *, int); +static PostProcessDoneCallback post_process_done_callback = NULL; + +typedef int (*PrintLog)(const char *, ...); +static PrintLog HTTPD_LOG_INFO = printf; +static PrintLog HTTPD_LOG_ERROR = printf; + +static char log_dir_path[128] = {0}; + +void *accept_request(void *arg); +void bad_request(int); +void cat(int, FILE *); +void cannot_execute(int); +int get_line(int, char *, int); +void headers(int, const char *); +void not_found(int); +void response_ok(int); +void serve_file(int, const char *); +void unimplemented(int); +void post_process(int, const char *, const char *, const char *); +void get_process(int, const char *, const char *, const char *); +int save_log_file(char *filename, char *buf, int len); + +void *accept_request(void *arg) +{ + int client = *(int *)arg; + char buf[1024]; + int numchars; + char method[255]; + char url[255]; + size_t i, j; + char *query_string = NULL; + + numchars = get_line(client, buf, sizeof(buf)); + i = 0; + j = 0; + + while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) + { + method[i] = buf[j]; + i++; + j++; + } + method[i] = '\0'; + + if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) + { + unimplemented(client); + close(client); + return NULL; + } + + i = 0; + while (ISspace(buf[j]) && (j < sizeof(buf))) + j++; + while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) + { + + url[i] = buf[j]; + i++; + j++; + } + url[i] = '\0'; + + if (strcasecmp(method, "GET") == 0) + { + query_string = url; + while ((*query_string != '?') && (*query_string != '\0')) + query_string++; + + if (*query_string == '?') + { + *query_string = '\0'; + query_string++; + } + } + + if (strcasecmp(method, "POST") == 0) + { + post_process(client, url, method, query_string); + response_ok(client); + } + else + { + get_process(client, url, method, query_string); + response_ok(client); + } + + close(client); + return NULL; +} + +void bad_request(int client) +{ + char buf[1024]; + + sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); + send(client, buf, sizeof(buf), MSG_NOSIGNAL); + sprintf(buf, "Content-type: text/html\r\n"); + send(client, buf, sizeof(buf), MSG_NOSIGNAL); + sprintf(buf, "\r\n"); + send(client, buf, sizeof(buf), MSG_NOSIGNAL); + sprintf(buf, "

Your browser sent a bad request, "); + send(client, buf, sizeof(buf), MSG_NOSIGNAL); + sprintf(buf, "such as a POST without a Content-Length.\r\n"); + send(client, buf, sizeof(buf), MSG_NOSIGNAL); +} + +void cat(int client, FILE *resource) +{ + char buf[1024]; + + fgets(buf, sizeof(buf), resource); + while (!feof(resource)) + { + send(client, buf, strlen(buf), MSG_NOSIGNAL); + fgets(buf, sizeof(buf), resource); + } +} + +void cannot_execute(int client) +{ + char buf[1024]; + + sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "Content-type: text/html\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "

Error prohibited CGI execution.\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); +} + +int save_log_file(char *filename, char *buf, int len) +{ + char path[255] = {0}; + sprintf(path, "%s%s", log_dir_path, filename); + FILE *fp = fopen(path, "a"); + if (fp == NULL) + { + HTTPD_LOG_ERROR("open log file %s failed.", path); + return -1; + } + if (fwrite(buf, 1, len, fp) != len) + { + HTTPD_LOG_ERROR("write log file %s failed.", path); + return -1; + } + if (fclose(fp) != 0) + { + HTTPD_LOG_ERROR("close log file %s failed.", path); + return -1; + } + return 0; +} + +void post_process(int client, const char *path, const char *method, const char *query_string) +{ + char buf[1024]; + char boundary[1024] = {0}; + char filename[1024] = {0}; + char *tmp; + int i; + int numchars = 1; + int content_length = -1; + + numchars = get_line(client, buf, sizeof(buf)); + + while ((numchars > 0) && strcmp("\n", buf)) + { + char tmp_char = buf[15]; + buf[15] = '\0'; + if (strcasecmp(buf, "Content-Length:") == 0) + { + content_length = atoi(&(buf[16])); + } + buf[15] = tmp_char; + + tmp_char = buf[13]; + buf[13] = '\0'; + if (strcasecmp(buf, "Content-Type:") == 0) + { + tmp = strstr(&buf[14], "boundary="); + if (tmp != NULL) + { + for (i = 0; i < 1023 && tmp[i + 9] != '\n'; i++) + { + boundary[i] = tmp[i + 9]; + } + boundary[i] = '\0'; + } + } + buf[13] = tmp_char; + numchars = get_line(client, buf, sizeof(buf)); + } + if (content_length == -1 || boundary[0] == '\0') + { + bad_request(client); + return; + } + + char *tmp_buf = malloc(4096 * 1024); + if (tmp_buf == NULL) + { + HTTPD_LOG_ERROR("malloc buffer failed."); + return; + } + memset(tmp_buf, 0, 4096 * 1024); + int total = 0; + while (1) + { + int n = recv(client, &tmp_buf[total], content_length, 0); + if (n < 0) + { + HTTPD_LOG_ERROR("recv data failed."); + free(tmp_buf); + return; + } + total += n; + if (total >= content_length) + { + break; + } + } + + tmp = strstr(tmp_buf, "filename=\""); + if (tmp == NULL) + { + HTTPD_LOG_ERROR("log filename not found."); + free(tmp_buf); + return; + } + + for (i = 0; i < 1023 && tmp[i + 10] != '\"'; i++) + { + filename[i] = tmp[i + 10]; + } + filename[i] = '\0'; + + char *data_start; + tmp = strstr(tmp_buf, "\r\n\r\n"); + if (tmp == NULL) + { + HTTPD_LOG_ERROR("log file data start flag failed."); + free(tmp_buf); + return; + } + data_start = &tmp[4]; + + char boundary_end[2048] = {0}; + sprintf(boundary_end, "--%s--", boundary); + char *data_end; + data_end = strstr(tmp_buf, boundary_end); + int data_len; + if (data_end == NULL) + { + HTTPD_LOG_ERROR("log file data end flag failed."); + free(tmp_buf); + return; + } + data_len = data_end - data_start - 2; + + HTTPD_LOG_INFO("recv log file datalen: %d.", data_len); + int ret = save_log_file(filename, data_start, data_len); + + if (post_process_done_callback != NULL) + { + post_process_done_callback(ret, filename, data_len); + } + + free(tmp_buf); +} + +void get_process(int client, const char *path, const char *method, const char *query_string) +{ + char buf[1024]; + int numchars = 1; + buf[0] = 'A'; + buf[1] = '\0'; + while ((numchars > 0) && strcmp("\n", buf)) + { + numchars = get_line(client, buf, sizeof(buf)); + // Todo... + } +} + +int get_line(int sock, char *buf, int size) +{ + int i = 0; + char c = '\0'; + int n; + + while ((i < size - 1) && (c != '\n')) + { + n = recv(sock, &c, 1, 0); + if (n > 0) + { + if (c == '\r') + { + n = recv(sock, &c, 1, MSG_PEEK); + if ((n > 0) && (c == '\n')) + recv(sock, &c, 1, 0); + else + c = '\n'; + } + buf[i] = c; + i++; + } + else + c = '\n'; + } + buf[i] = '\0'; + + return (i); +} + +void headers(int client, const char *filename) +{ + char buf[1024]; + (void)filename; + + strcpy(buf, "HTTP/1.0 200 OK\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + + strcpy(buf, SERVER_STRING); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "Content-Type: text/html\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + strcpy(buf, "\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); +} + +void response_ok(int client) +{ + char buf[1024]; + sprintf(buf, "HTTP/1.0 200 OK\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); +} + +void not_found(int client) +{ + char buf[1024]; + + sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + + sprintf(buf, SERVER_STRING); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "Content-Type: text/html\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "Not Found\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "

The server could not fulfill\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "your request because the resource specified\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "is unavailable or nonexistent.\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); +} + +void serve_file(int client, const char *filename) +{ + FILE *resource = NULL; + int numchars = 1; + char buf[1024]; + + buf[0] = 'A'; + buf[1] = '\0'; + while ((numchars > 0) && strcmp("\n", buf)) + numchars = get_line(client, buf, sizeof(buf)); + + resource = fopen(filename, "r"); + if (resource == NULL) + not_found(client); + else + { + headers(client, filename); + + cat(client, resource); + } + fclose(resource); +} + +void unimplemented(int client) +{ + char buf[1024]; + + sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, SERVER_STRING); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "Content-Type: text/html\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "Method Not Implemented\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "

HTTP request method not supported.\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); + sprintf(buf, "\r\n"); + send(client, buf, strlen(buf), MSG_NOSIGNAL); +} + +int RegisterPostProcessDoneCallback(PostProcessDoneCallback callback) +{ + post_process_done_callback = callback; + return 0; +} + +int RegisterLogFunc(struct PrintLogFuncSet funcSet) +{ + if (funcSet.errorLog != NULL && funcSet.infoLog != NULL) + { + HTTPD_LOG_ERROR = funcSet.errorLog; + HTTPD_LOG_INFO = funcSet.infoLog; + return 0; + } + return -1; +} + +int SetLogSavePath(const char *path) +{ + if (strlen(path) >= 128) + { + HTTPD_LOG_ERROR("log save path is too long."); + return -1; + } + strcpy(log_dir_path, path); + return 0; +} + +int StartHttpServerSync(uint16_t port) +{ + int server_sock = -1; + int client_sock = -1; + struct sockaddr_in client_name; + int client_name_len = sizeof(client_name); + pthread_t newthread; + struct sockaddr_in name; + + server_sock = socket(PF_INET, SOCK_STREAM, 0); + if (server_sock == -1) + { + HTTPD_LOG_ERROR("socket create failed."); + return -1; + } + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = htons(port); + name.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(server_sock, (struct sockaddr *)&name, sizeof(name)) < 0) + { + HTTPD_LOG_ERROR("bind failed."); + close(server_sock); + return -1; + } + if (port == 0) + { + int namelen = sizeof(name); + if (getsockname(server_sock, (struct sockaddr *)&name, &namelen) == -1) + { + HTTPD_LOG_ERROR("getsockname failed."); + close(server_sock); + return -1; + } + port = ntohs(name.sin_port); + } + if (listen(server_sock, 20) < 0) + { + HTTPD_LOG_ERROR("listen failed."); + close(server_sock); + return -1; + } + + HTTPD_LOG_INFO("simplehttpd running on port %d.", port); + + while (1) + { + client_sock = accept(server_sock, (struct sockaddr *)&client_name, &client_name_len); + if (client_sock == -1) + { + HTTPD_LOG_ERROR("accept failed."); + close(server_sock); + return -1; + } + + if (pthread_create(&newthread, NULL, accept_request, (void *)&client_sock) != 0) + { + close(server_sock); + return -1; + } + } + + close(server_sock); + return -1; +} diff --git a/simplehttpd.h b/simplehttpd.h new file mode 100644 index 0000000000000000000000000000000000000000..e73a2f673dac841c6b57130a982c438e04055a74 --- /dev/null +++ b/simplehttpd.h @@ -0,0 +1,31 @@ +#ifndef HTTPD_H +#define HTTPD_H + +#include + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif +typedef void (*PostProcessDoneCallback)(int code, const char * filename, int datalen); +typedef int (*PrintLog)(const char* format, ...); + +struct PrintLogFuncSet +{ + PrintLog infoLog; + PrintLog errorLog; +}; + +int RegisterPostProcessDoneCallback(PostProcessDoneCallback callback); +int RegisterLogFunc(struct PrintLogFuncSet funcSet); +int SetLogSavePath(const char *path); +int StartHttpServerSync(uint16_t port); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif //HTTPD_H \ No newline at end of file diff --git a/test.c b/test.c new file mode 100644 index 0000000000000000000000000000000000000000..f32a5402e4f7c5d7d2965c72902dacc1cb74bdf6 --- /dev/null +++ b/test.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include "simplehttpd.h" + +void ProcessDoneCallback(int code, const char *filename, int datalen) +{ + printf("code: %d, filename: %s, datalen: %d\n", code, filename, datalen); +} + +int main() +{ + int ret; + ret = RegisterPostProcessDoneCallback(ProcessDoneCallback); + printf("RegisterPostProcessDoneCallback ret=%d\n", ret); + ret = SetLogSavePath("/home/tongkang/"); + printf("SetLogSavePath ret=%d\n", ret); + ret = StartHttpServerSync(9999); + return 0; +} \ No newline at end of file