├── .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 |
--------------------------------------------------------------------------------