├── .config.txt.swp ├── include ├── tokenize.h ├── fileparser.h ├── log.h └── httpconf.h ├── config.txt ├── index.html ├── Makefile ├── src ├── file_parser.c ├── tokenize.c ├── listener.c ├── http_config.c └── http_server.c └── README /.config.txt.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtungatkar/Simple-HTTP-server/HEAD/.config.txt.swp -------------------------------------------------------------------------------- /include/tokenize.h: -------------------------------------------------------------------------------- 1 | #ifndef __TOKENIZE_HDR__ 2 | #define __TOKENIZE_HDR__ 3 | #define MAX_TOKEN_SIZE 1024 4 | 5 | int tokenize(char **str, char *dest); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /include/fileparser.h: -------------------------------------------------------------------------------- 1 | #ifndef __FILE_PARSER_HDR__ 2 | #define __FILE_PARSER_HDR__ 3 | int file_parser(char *filename, int (*reader)(void *, char *line), void *c); 4 | #define MAX_LINE 1024 5 | #endif 6 | -------------------------------------------------------------------------------- /config.txt: -------------------------------------------------------------------------------- 1 | Listen 8080 2 | #comment 3 | DocumentRoot /home/gaurav/Dropbox/Code 4 | 5 | DirectoryIndex index.html 6 | 7 | .htm text/html 8 | .html text/html 9 | .txt text/plain 10 | .jpeg image/jpeg 11 | .jpg image/jpeg 12 | .png image/png 13 | .gif image/gif 14 | .bmp image/bmp 15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | .: Game Land :. © Graformix 7 | 8 | 9 | 10 | JUST A TEST FILE! 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOGGER__ 2 | #define __LOGGER__ 3 | #ifndef TRACE 4 | #define TRACE 1 5 | #endif 6 | #include 7 | #define LOG(_hdl, _msg) \ 8 | { \ 9 | if(TRACE > 0) \ 10 | fprintf(_hdl,"Pid=%d:%s %s",getpid(),__FUNCTION__, _msg); \ 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJ_DIR = . 2 | INCLUDE_DIR = $(PROJ_DIR)/include 3 | SRC_DIR = $(PROJ_DIR)/src 4 | OBJ_DIR = $(PROJ_DIR)/obj 5 | 6 | INCLUDE = -I $(INCLUDE_DIR) 7 | CFLAGS = -march=i586 -fno-builtin -c -Wall $(INCLUDE) 8 | CC = /usr/bin/gcc 9 | BIN = http_server 10 | 11 | SRC = $(SRC_DIR)/http_server.c \ 12 | $(SRC_DIR)/listener.c \ 13 | $(SRC_DIR)/tokenize.c \ 14 | $(SRC_DIR)/file_parser.c \ 15 | $(SRC_DIR)/http_config.c 16 | 17 | #OBJ = ${SRC:%.c=%.o} 18 | OBJ = $(OBJ_DIR)/http_server.o \ 19 | $(OBJ_DIR)/listener.o \ 20 | $(OBJ_DIR)/tokenize.o \ 21 | $(OBJ_DIR)/file_parser.o \ 22 | $(OBJ_DIR)/http_config.o 23 | #---make targets 24 | all: $(BIN) 25 | #%.o: %.c 26 | # $(CC) $(CFLAGS) -c $< -o $(OBJ_DIR)/$@ 27 | 28 | $(OBJ_DIR)/http_server.o: $(SRC_DIR)/http_server.c 29 | $(CC) -c $(CFLAGS) $< -o $@ 30 | 31 | $(OBJ_DIR)/http_config.o: $(SRC_DIR)/http_config.c 32 | $(CC) -c $(CFLAGS) $< -o $@ 33 | $(OBJ_DIR)/listener.o: $(SRC_DIR)/listener.c 34 | $(CC) -c $(CFLAGS) $< -o $@ 35 | $(OBJ_DIR)/tokenize.o: $(SRC_DIR)/tokenize.c 36 | $(CC) -c $(CFLAGS) $< -o $@ 37 | $(OBJ_DIR)/file_parser.o: $(SRC_DIR)/file_parser.c 38 | $(CC) -c $(CFLAGS) $< -o $@ 39 | 40 | http_server: $(OBJ) 41 | $(CC) -o $(OBJ_DIR)/$(BIN) $(OBJ) 42 | 43 | .PHONY : clean 44 | clean: 45 | rm -f $(OBJ_DIR)/*.o 46 | rm -f $(OBJ_DIR)/http_server 47 | -------------------------------------------------------------------------------- /src/file_parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File Name : file_parser.c 3 | * Author : Gaurav Tungatkar 4 | * Creation Date : 16-01-2011 5 | * Description : 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "log.h" 18 | #include "fileparser.h" 19 | 20 | /* file_parser() - Parse a file line by line 21 | * Parameters: 22 | * filename - name of file to parse 23 | * reader - user defined routine to handle the parsed line. It is fed the 24 | * current parsed line 25 | * c - void* user defined data that will be passed to reader() 26 | */ 27 | int file_parser(char *filename, int (*reader)(void *, char *line), void *c) 28 | { 29 | 30 | FILE *fp; 31 | char line[MAX_LINE]; 32 | assert(filename != NULL); 33 | assert(c != NULL); 34 | if((fp = fopen(filename, "r")) == NULL) 35 | { 36 | LOG(stdout, "Failed to open config file\n"); 37 | return -1; 38 | } 39 | while((fgets(line, MAX_LINE, fp) != NULL)) 40 | { 41 | if(reader(c, line) == -1) 42 | { 43 | LOG(stdout, "file parser: reader() failed\n"); 44 | return -1; 45 | } 46 | } 47 | fclose(fp); 48 | return 0; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/tokenize.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File Name : tokenize.c 3 | * Author : Gaurav Tungatkar 4 | * Creation Date : 14-01-2011 5 | * Description : 6 | * 7 | */ 8 | #include 9 | #include 10 | #include "tokenize.h" 11 | /* 12 | * tokenize() 13 | * str = pointer to string that has to be tokenized 14 | * dest = destination where separate token will be stored 15 | * 16 | * This function separates tokens from the given string. Tokens can be of 17 | * maximum MAX_TOKEN_SIZE . After a call to tokenize, str points to the first 18 | * character after the current token; the string is consumed by the tokenize 19 | * routine. 20 | * RETURNS: length of current token 21 | * -1 if error 22 | */ 23 | int tokenize(char **str, char *dest) 24 | { 25 | int count = 0; 26 | while(isspace(**str)) 27 | (*str)++; 28 | while(!isspace(**str) && (**str != '\0')) 29 | { 30 | *dest++ = *(*str)++; 31 | count++; 32 | if(count >= MAX_TOKEN_SIZE) 33 | { 34 | count = -1; 35 | break; 36 | } 37 | } 38 | *dest = '\0'; 39 | return count; 40 | } 41 | #define main nomain 42 | int main() 43 | { 44 | char *str = " Do I get all \n \n ninechars ninetyninechars "; 45 | char dest[MAX_TOKEN_SIZE]; 46 | int ret; 47 | while((ret = tokenize(&str, dest))> 0) 48 | { 49 | printf("token = %s\n", dest); 50 | } 51 | if(ret == -1) 52 | printf("Tokenization failed to complete\n"); 53 | return 0; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /include/httpconf.h: -------------------------------------------------------------------------------- 1 | #ifndef __HTTPCONF__ 2 | #define __HTTPCONF__ 3 | 4 | #define MAX_BUF 1024 5 | #define PENDING_CONN 64 6 | 7 | #include "log.h" 8 | 9 | /* various config element types parsed from config file*/ 10 | enum cfg_elements_t { 11 | FILE_TYPE, /*type of file*/ 12 | DOCUMENT_ROOT, 13 | DIR_INDEX, 14 | LISTEN_PORT, 15 | FILE_TYPE_EXT, /*Extension for the file type*/ 16 | DEFAULT 17 | }; 18 | struct valid_file_types { 19 | char extension[MAX_BUF]; /*extension of file*/ 20 | char type[MAX_BUF]; /*type of the file contents*/ 21 | }; 22 | struct directory_index_file { 23 | char filename[MAX_BUF]; /*default file name used when only directory is 24 | specified*/ 25 | }; 26 | struct http_server_config { 27 | int listen_port; /*server listens on this port*/ 28 | char document_root[MAX_BUF]; /*root directory */ 29 | struct valid_file_types filetypes[MAX_BUF]; /*supported file types */ 30 | int f_type_cnt; /* total number of supported 31 | file types*/ 32 | struct directory_index_file dir_index[2]; /*default index file*/ 33 | 34 | }; 35 | 36 | /*Not used currently. Convenience data structure in case threads need to be used*/ 37 | struct http_server_data { 38 | struct http_server_config *cfg; 39 | int sockfd; 40 | }; 41 | 42 | /*Function declarations */ 43 | 44 | int cfg_reader(void *c, char *line); 45 | void http_server(struct http_server_config *cfg, int sockfd); 46 | 47 | int valid_method_string(char **request, char *request_method); 48 | int valid_version(char **request, struct http_server_config *cfg, 49 | char *version); 50 | 51 | int valid_uri(char **request, struct http_server_config *cfg, 52 | char *uri); 53 | int valid_filetype(char **request, struct http_server_config *cfg, 54 | char *uri); 55 | int connection_handler(struct http_server_config *cfg); 56 | #endif 57 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | *****Simple HTTP Web Server***** 2 | ******************************* 3 | 4 | Author: Gaurav Tungatkar 5 | 6 | Description: 7 | A simple HTTP server written in C, using sockets, that serves static web pages. 8 | Along with the server engine, some additional utilities like the fileparser, 9 | connection handler, tokenizer that may be helpful later in other projects. 10 | 11 | Code organization and directory structure: 12 | 13 | Project1 14 | |__ src - All .c source files in this directory 15 | |__ include - All header files in this directory 16 | |_Makefile, config.txt - These files at the same level 17 | 18 | tokenize.c -utility function to separate a line into tokens 19 | fileparser.c -utility function to parse a file line by line 20 | http_config.c -Handles reading of config file, error checking and populating 21 | in-memory config object. 22 | http_server.c -Main http server engine 23 | listener.c -Connection handler routine 24 | 25 | Instructions to compile: 26 | 1. Go to base project folder Project1. 27 | 2. make 28 | 29 | Instructions to run: 30 | 1. From Project1 folder 31 | ./obj/http_server [config_file] 32 | 33 | Config file path is optional. It may be specified at the command line. 34 | If not provided, following assumptions hold:- 35 | a. config.txt located in Project1 directory is the default config file 36 | b. The config.txt file must be located in the cwd(current working 37 | directory from which server is run) 38 | 39 | Technique to handle concurrent requests: 40 | A new process is "fork"ed on receiving a request. The newly forked server 41 | instance then processes this request, while the original process keeps listening 42 | for new connections. Though fork can be costly, it allows a simpler model of 43 | implementation without needing locking(in case of threads), etc. 44 | However, the implementation is modular enough so that a pthread based 45 | server can be implemented with minimal change. 46 | 47 | Limitations: 48 | Client may send requests in HTTP/1.0 or HTTP/1.1 version. However, server response is always HTTP/1.0 49 | This folows the Robustness principle given in rfc 2145 http://www.ietf.org/rfc/rfc2145.txt 50 | 51 | Logging: 52 | Logs can be turned off by defining TRACE to be 0 in log.h at compile time. 53 | All logs are currently directed to stdout. 54 | -------------------------------------------------------------------------------- /src/listener.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File Name : listener.c 3 | * Author : Gaurav Tungatkar 4 | * Creation Date : 16-01-2011 5 | * Description : 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "httpconf.h" 17 | 18 | 19 | int connection_handler(struct http_server_config *cfg) 20 | { 21 | int listen_fd, new_fd, set = 1; 22 | struct sockaddr_in listener_addr, client_addr; 23 | socklen_t addr_len = 0; 24 | pid_t pid; 25 | char p[10]; 26 | assert(cfg != NULL); 27 | /* Standard server side socket sequence*/ 28 | 29 | if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 30 | { 31 | LOG(stdout, "socket() failure\n"); 32 | return -1; 33 | } 34 | if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &set, 35 | sizeof(int)) == -1) { 36 | LOG(stdout, "setsockopt() failed"); 37 | return -1; 38 | } 39 | bzero(&listener_addr, sizeof(listener_addr)); 40 | listener_addr.sin_family = AF_INET; 41 | listener_addr.sin_port = htons(cfg->listen_port); 42 | listener_addr.sin_addr.s_addr = htonl(INADDR_ANY); 43 | memset(listener_addr.sin_zero, '\0', sizeof(listener_addr.sin_zero)); 44 | 45 | if(bind(listen_fd, (struct sockaddr*)&listener_addr, sizeof 46 | listener_addr) == -1) 47 | { 48 | LOG(stdout, "bind() failed\n"); 49 | return -1; 50 | 51 | } 52 | 53 | if(listen(listen_fd, PENDING_CONN) == -1) 54 | { 55 | LOG(stdout, "listen() failed\n"); 56 | return -1; 57 | } 58 | sprintf(p, "HTTP server listening on port:%d\n", cfg->listen_port); 59 | LOG(stdout, p); 60 | while(1) 61 | { 62 | 63 | new_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &addr_len); 64 | if(new_fd == -1) 65 | { 66 | //log 67 | LOG(stdout, "accept() failed\n"); 68 | return -1; 69 | 70 | } 71 | LOG(stdout, "new connection accepted\n"); 72 | //fork a new process to handle this request 73 | if((pid = fork()) == -1) 74 | { 75 | //LOG Error 76 | LOG(stdout, "Error in fork\n"); 77 | 78 | } 79 | else if(pid == 0) 80 | { 81 | /*This is the child process. This will service the 82 | *request while parent goes back to listening.*/ 83 | LOG(stdout, "Servicing request\n"); 84 | http_server(cfg, new_fd); 85 | return 0; 86 | } 87 | else 88 | { 89 | close(new_fd); 90 | } 91 | 92 | 93 | } 94 | 95 | return 0; 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/http_config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File Name : http_config.c 3 | * Author : Gaurav Tungatkar 4 | * Creation Date : 14-01-2011 5 | * Description : 6 | * 7 | */ 8 | /*Read http server configuration from the config file and store it in memory 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "log.h" 18 | #include "tokenize.h" 19 | #include "httpconf.h" 20 | #include "fileparser.h" 21 | 22 | int cfg_reader(void *c, char *line) 23 | { 24 | assert(c); 25 | assert(line); 26 | char token[MAX_TOKEN_SIZE]; 27 | int next_token = DEFAULT; 28 | struct http_server_config *cfg = (struct http_server_config *)c; 29 | int indx = 0; 30 | int config_cnt = 0; 31 | /* From the given config file format, we assume we have 4 distinct 32 | * config objects - port, DocumentRoot, DirectoryIndex, supported files 33 | * and all are mandatory. If one of them is missing, we should give an 34 | * error and return. 35 | * This function gets one line of the config file at a time, which is 36 | * further parses. 37 | */ 38 | 39 | while(tokenize(&line, token) > 0) 40 | { 41 | if(token[0] == '#') 42 | { 43 | //This line must be a Comment. Ignore and return 44 | return 0; 45 | } 46 | if(strcmp(token, "DocumentRoot") == 0) 47 | { 48 | next_token = DOCUMENT_ROOT; 49 | config_cnt++; 50 | 51 | 52 | } 53 | else if(strcmp(token, "Listen") == 0) 54 | { 55 | next_token = LISTEN_PORT; 56 | config_cnt++; 57 | 58 | } 59 | else if(strcmp(token, "DirectoryIndex") == 0) 60 | { 61 | next_token = DIR_INDEX; 62 | config_cnt++; 63 | } 64 | else //list of valid files 65 | { 66 | int len; 67 | char *p; 68 | if(token[0] == '.') 69 | { 70 | next_token = FILE_TYPE; 71 | } 72 | len = strlen(token); 73 | /* next_token was set previously based on the type of 74 | * config object. Based on that type, we now store its 75 | * value 76 | */ 77 | switch(next_token) 78 | { 79 | case DOCUMENT_ROOT: 80 | if(len > sizeof(cfg->document_root)) 81 | { 82 | LOG(stdout, 83 | "config:DocumentRoot size exceeded\n"); 84 | return -1; 85 | 86 | } 87 | strcpy(cfg->document_root, token); 88 | config_cnt++; 89 | next_token = DEFAULT; 90 | break; 91 | case LISTEN_PORT: 92 | cfg->listen_port = strtol(token, &p, 10); 93 | if(cfg->listen_port > 65535) 94 | { 95 | LOG(stdout, 96 | "Port value invalid\n"); 97 | return -1; 98 | } 99 | config_cnt++; 100 | next_token = DEFAULT; 101 | break; 102 | case DIR_INDEX: 103 | if(len > 104 | sizeof(cfg->dir_index[0].filename)) 105 | { 106 | //LOG 107 | LOG(stdout, 108 | "config:DirectoryIndex size exceeded\n"); 109 | return -1; 110 | } 111 | strcpy(cfg->dir_index[indx++].filename, 112 | token); 113 | config_cnt++; 114 | next_token = DIR_INDEX; 115 | break; 116 | case FILE_TYPE: 117 | if(len > 118 | sizeof(cfg->filetypes[0].extension)) 119 | { 120 | 121 | LOG(stdout, 122 | "config:File type size exceeded\n"); 123 | return -1; 124 | 125 | } 126 | strcpy(cfg->filetypes[cfg->f_type_cnt].extension,token); 127 | next_token = FILE_TYPE_EXT; 128 | break; 129 | case FILE_TYPE_EXT: 130 | if(len > 131 | sizeof(cfg->filetypes[0].type)) 132 | { 133 | //LOG 134 | LOG(stdout, 135 | "config:Extension size exceeded\n"); 136 | return -1; 137 | 138 | } 139 | strcpy(cfg->filetypes[cfg->f_type_cnt].type,token); 140 | cfg->f_type_cnt++; 141 | next_token = DEFAULT; 142 | break; 143 | default: 144 | LOG(stdout, 145 | "Error in config file.Exiting...\n"); 146 | return -1; 147 | 148 | } 149 | 150 | } 151 | 152 | } 153 | /* config_cnt counts how many config types or values we have seen. 154 | * if it is 1, it means we just got the type and not the value.*/ 155 | if(config_cnt == 1) 156 | { 157 | LOG(stdout, "Error in config file\n"); 158 | return -1; 159 | 160 | } 161 | return 0; 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/http_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File Name : http_server.c 3 | * Author : Gaurav Tungatkar 4 | * Creation Date : 17-01-2011 5 | * Description : 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "httpconf.h" 17 | #include "tokenize.h" 18 | #include "fileparser.h" 19 | #define BUFFSIZE (4*1024) 20 | #define MAX_FILENAME 512 21 | #define HTTP_HDR_SIZE 512 22 | #define HTTP_URI_SIZE 1024 23 | #define HTTP_STATUS_SIZE 1024 24 | #define HTTP_GET 11 25 | #define SP 0x20 26 | #define CRLF "\r\n" 27 | void http_server(struct http_server_config *cfg, int sockfd) 28 | { 29 | 30 | char request[BUFFSIZE+1]; 31 | int numbytes; 32 | int errflag = 0; 33 | char request_method[5]; 34 | char version[10]; 35 | int method; 36 | int ftype;; 37 | int blksize = 0; 38 | int fd; 39 | char uri[HTTP_URI_SIZE]; 40 | char filepath[HTTP_URI_SIZE]; 41 | char status[HTTP_STATUS_SIZE]; 42 | char header[HTTP_HDR_SIZE], buffer[BUFFSIZE]; 43 | if((numbytes = read(sockfd, (void *)request, BUFFSIZE)) <= 0) 44 | { 45 | LOG(stdout, "read from socket failed"); 46 | return; 47 | } 48 | char *requestptr = request; 49 | if((method = valid_method_string(&requestptr, request_method)) == -1) 50 | { 51 | //ERROR in Request 52 | snprintf(status, 53 | HTTP_STATUS_SIZE, 54 | "HTTP/1.0 400 Bad Request: Invalid Method: %s\r\n", 55 | request_method); 56 | LOG(stdout, status); 57 | errflag = 1; 58 | } 59 | if(!errflag && (method == HTTP_GET)) 60 | { 61 | //tokenize URI 62 | //check that the method name and URI are separated by exactly 1 63 | //SP character 64 | //requestptr should now be pointing at a SP character. If the 65 | //next character is SP as well, invalid request 66 | if(valid_uri(&requestptr, cfg, uri) == -1) 67 | { 68 | snprintf(status, 69 | HTTP_STATUS_SIZE, 70 | "HTTP/1.0 400 Bad Request: Invalid URI: %s\r\n", 71 | uri); 72 | LOG(stdout, status); 73 | //ERROR in request 74 | errflag = 1; 75 | 76 | } 77 | 78 | 79 | } 80 | if(!errflag) 81 | { 82 | if(valid_version(&requestptr, cfg, version) == -1) 83 | { 84 | //ERROR 85 | //HTTP/1.0 400 Bad Request: Invalid HTTP-Version: 86 | // 87 | snprintf(status, 88 | HTTP_STATUS_SIZE, 89 | "HTTP/1.0 400 Bad Request: Invalid HTTP-Version: %s\r\n", 90 | version); 91 | LOG(stdout, status); 92 | errflag = 1; 93 | } 94 | 95 | } 96 | if(!errflag) 97 | { 98 | if((ftype = valid_filetype(&requestptr, cfg, uri)) == -1) 99 | { 100 | //ERROR 101 | snprintf(status, 102 | HTTP_STATUS_SIZE, 103 | "HTTP/1.0 501 Not Implemented: %s\r\n", 104 | uri); 105 | LOG(stdout, status); 106 | errflag = 1; 107 | } 108 | 109 | } 110 | //seems like request came up fine! Now lets see if we can read the file 111 | if(!errflag) 112 | { 113 | /*all file paths relative to document root */ 114 | strncat(filepath, cfg->document_root, HTTP_URI_SIZE); 115 | strncat(filepath, uri, HTTP_URI_SIZE); 116 | fd = open(filepath, O_RDONLY); 117 | if(fd == -1) 118 | { 119 | snprintf(status,HTTP_STATUS_SIZE, 120 | "HTTP/1.0 404 Not Found: %s\r\n", 121 | uri); 122 | LOG(stdout, status); 123 | errflag = 1; 124 | //file not found.. 125 | 126 | } 127 | 128 | } 129 | if(!errflag) 130 | { 131 | int filelen; 132 | //find file size 133 | if((filelen = lseek(fd, 0, SEEK_END)) < 0) 134 | { 135 | //error 136 | LOG(stdout, "lseek() failed\n"); 137 | } 138 | snprintf(status, HTTP_STATUS_SIZE, 139 | "HTTP/1.0 200 Document Follows\r\n"); 140 | LOG(stdout, status); 141 | LOG(stdout, filepath); 142 | snprintf(header, HTTP_HDR_SIZE, 143 | "Content-Type: %s\r\nContent-Length: %d\r\n\n", 144 | cfg->filetypes[ftype].type,filelen ); 145 | lseek(fd, 0, SEEK_SET); 146 | /*send status and header */ 147 | write(sockfd, status, strlen(status)); 148 | write(sockfd, header, strlen(header)); 149 | /*write the file contents to socket */ 150 | while ( (blksize = read(fd, buffer, BUFFSIZE)) > 0 ) { 151 | write(sockfd,buffer,blksize); 152 | } 153 | 154 | 155 | 156 | } 157 | else 158 | { 159 | /*Request had error. Send the appropriate status*/ 160 | write(sockfd, status, strlen(status)); 161 | LOG(stdout, "Error in processing request\n"); 162 | 163 | } 164 | 165 | 166 | 167 | 168 | return; 169 | } 170 | int valid_method_string(char **request, char *request_method) 171 | { 172 | /*only GET method supported */ 173 | if((tokenize(request, request_method) != 3) 174 | ||(strcmp(request_method, "GET") != 0)) 175 | { 176 | LOG(stdout, "Invalid method\n"); 177 | return -1; 178 | } 179 | else 180 | { 181 | return HTTP_GET; 182 | } 183 | 184 | } 185 | int valid_version(char **request, struct http_server_config *cfg, 186 | char *version) 187 | { 188 | /* HTTP versions 1.0 and 1.1 messages are accepted 189 | */ 190 | if((tokenize(request, version) <= 0) 191 | ||((strcmp(version, "HTTP/1.1") != 0) && (strcmp(version, 192 | "HTTP/1.0") != 0))) 193 | { 194 | LOG(stdout, "Version not supported\n"); 195 | return -1; 196 | } 197 | else 198 | { 199 | return 0; 200 | } 201 | 202 | 203 | } 204 | int valid_uri(char **request, struct http_server_config *cfg, 205 | char *uri) 206 | { 207 | /*if it sees 2 or more leading spaces(SP) - thats invalid URI*/ 208 | if(*(*(request)+1) == SP) 209 | { 210 | LOG(stdout, "Invalid URI\n"); 211 | return -1; 212 | } 213 | 214 | if((tokenize(request, uri) <= 0)) 215 | { 216 | LOG(stdout, "Invalid URI\n"); 217 | return -1; 218 | } 219 | else 220 | { 221 | //cannot refer to the parent directory 222 | if(uri[0] == '.' && uri[1] == '.') 223 | { 224 | LOG(stdout, "Invalid URI\n"); 225 | return -1; 226 | } 227 | //if just '/' , append the default index file name 228 | if((uri[0] == '/') && (uri[1] == '\0')) 229 | strcat(uri, 230 | cfg->dir_index[0].filename); 231 | } 232 | return 0; 233 | 234 | } 235 | 236 | int valid_filetype(char **request, struct http_server_config *cfg, 237 | char *uri) 238 | { 239 | int i = 0, validstr = -1; 240 | int j; 241 | /* entry in the form of 242 | * .html text/html 243 | * So get the extension. */ 244 | while(uri[i] != '.') 245 | { 246 | if(uri[i] != '\0') 247 | i++; 248 | else 249 | return 0; 250 | 251 | } 252 | /*Check if this extension is present in supported extensions*/ 253 | for(j = 0; j < cfg->f_type_cnt; j++) 254 | { 255 | if(!strcmp((uri+i), cfg->filetypes[j].extension)) 256 | validstr = j; 257 | } 258 | if(validstr < 0) 259 | { 260 | LOG(stdout, "Invalid filetype\n"); 261 | return -1; 262 | } 263 | return validstr; 264 | 265 | } 266 | int main(int argc, char *argv[]) 267 | { 268 | struct http_server_config cfg ; 269 | memset(&cfg, 0, sizeof(cfg)); 270 | char filename[MAX_FILENAME]; 271 | if(argc == 2) 272 | { 273 | if(strlen(argv[1]) < MAX_FILENAME) 274 | strcpy(filename, argv[1]); 275 | } 276 | else 277 | strcpy(filename, "config.txt"); 278 | 279 | if(file_parser(filename, cfg_reader, &cfg)== -1) 280 | { 281 | LOG(stdout, "Configuration Error.Exiting...\n"); 282 | exit(1); 283 | 284 | } 285 | /*Ignore child death and avoid zombies!*/ 286 | signal(SIGCLD, SIG_IGN); 287 | LOG(stdout, 288 | "Starting primitive HTTP web server implemented for CSC573\n"); 289 | if(connection_handler(&cfg) == -1) 290 | { 291 | LOG(stdout, "Error!Exiting..\n"); 292 | exit(1); 293 | } 294 | return 0; 295 | } 296 | --------------------------------------------------------------------------------