-
Notifications
You must be signed in to change notification settings - Fork 1
/
webserv.c
executable file
·321 lines (273 loc) · 8.52 KB
/
webserv.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <dirent.h>
#include <sched.h>
#include <signal.h>
// Function to get file extension from given path
// Credit: ThiefMaster on StackOverflow (http://stackoverflow.com/questions/5309471/getting-file-extension-in-c)
char *getFileExtension(char *filename) {
char *dot = strrchr(filename, '.');
if(!dot || dot == filename) {
return "";
}
return dot + 1;
}
// Function to get the path from the request
char *getRequestPath(char *request, int inset) {
char *filePath = malloc(256);
// Go through each character, beginning at the inset ("GET /") and stopping at space, new line, end, or parameter
int i;
for (i = inset; request[i] != ' ' && request[i] != '\n' && request[i] != '\0' && request[i] != '?'; i++) {
filePath[i - inset] = request[i];
}
filePath[i - inset] = 0;
return filePath;
}
// Function to get the parameters from the request, add to array
int getRequestParameters(char const *request, char *array[]) {
// Get the string after '?'
char *truncatedString = strstr(request, "?");
truncatedString++;
char *parameterString = malloc(256);
// Return if no parameters
if (truncatedString == NULL) return 0;
// Go through the string, stopping at space, newline, or terminating character
int i;
for (i = 0; truncatedString[i] != ' ' && request[i] != '\n' && request[i] != '\0'; i++) {
parameterString[i] = truncatedString[i];
}
parameterString[i] = 0;
// Tokenize the parameters
i = 0;
char *token = strtok(parameterString, "&");
while (token != NULL) {
array[i++] = token;
token = strtok(NULL, "&");
}
return i;
}
// Function to print http error and end
void throwHttpError(int errorCode, int client){
char *buffer = malloc(256);
switch(errorCode){
case 404:
sprintf(buffer, "HTTP/ 1.1 404 NOT FOUND\n");
write(client, buffer, strlen(buffer));
sprintf(buffer, "Content-Type: text/html\n\n");
write(client, buffer, strlen(buffer));
sprintf(buffer, "<html><head><title> 404 NOT FOUND</title></head>");
write(client, buffer, strlen(buffer));
sprintf(buffer, "<body><h1>404 NOT FOUND</h1></body></html>\n");
write(client, buffer, strlen(buffer));
break;
case 501:
sprintf(buffer, "HTTP/ 1.1 501 NOT IMPLEMENTED\n");
write(client, buffer, strlen(buffer));
sprintf(buffer, "Content-Type: text/html\n\n");
write(client, buffer, strlen(buffer));
sprintf(buffer, "<html><head><title> 501 NOT IMPLEMENTED</title></head>");
write(client, buffer, strlen(buffer));
sprintf(buffer, "<body><h1>501 NOT IMPLEMENTED</h1></body></html>\n");
write(client, buffer, strlen(buffer));
break;
default:
break;
}
close(client);
exit(0);
}
// Function to handle a request from a client
int getRequest(int client) {
char output[4096];
// Read the request from the client into the buffer
char buffer[1024];
int re = read(client, buffer, 1024);
// Throw error if not GET request
if (strncmp(buffer, "GET ", 4) != 0) {
throwHttpError(501, client);
exit(1);
}
// Check if directory listing of root directory
if (strncmp(buffer, "GET / ", 6) == 0) {
strcpy(buffer, "GET /. ");
}
// Get the path from the request
char *filePath = getRequestPath(buffer, 5);
// Check if file at path exists
FILE *file;
if ((file = fopen(filePath, "r")) == NULL) {
throwHttpError(404, client);
exit(1);
}
// Write success message
sprintf(output, "HTTP/1.1 200 OK\r\n");
write(client, output, strlen(output));
// Get the file type from the extension
char *fileType = getFileExtension(filePath);
// Compare fileType to the different supported filetypes
if (strcmp(fileType, "jpg") == 0 || strcmp(fileType, "jpeg") == 0 || strcmp(fileType, "gif") == 0 || strcmp(fileType, "png") == 0) {
// Write the content type
if (strcmp(fileType, "gif") == 0) {
sprintf(output, "Content-Type: image/gif\n\n");
} else if (strcmp(fileType, "png") == 0) {
sprintf(output, "Content-Type: image/png\n\n");
} else {
sprintf(output, "Content-Type: image/jpeg\n\n");
}
write(client, output, strlen(output));
// Get file descriptor for file at path
int img = open(filePath, O_RDONLY, 0);
// Get information about the image
struct stat stat_struct;
if (fstat(img, &stat_struct) == -1) {
perror("* ERROR: image stat issue\n");
}
int imgSize = stat_struct.st_size;
if (imgSize == -1) {
perror("* ERROR: image size issue\n");
}
// Send image
size_t total = 0;
ssize_t bytesSent;
while(total < imgSize) {
bytesSent = sendfile(client, img, 0, imgSize-total);
if (bytesSent <= 0) {
perror("* ERROR: image transfer issue\n");
}
total += bytesSent;
}
} else if (strcmp(fileType, "html") == 0 || (strcmp(fileType, "txt") == 0)) {
// Write the content type
if ((strcmp(fileType, ".txt") == 0)) {
sprintf(output, "Content-Type: text/plain\n\n");
} else {
sprintf(output, "Content-Type: text/html\n\n");
}
write(client, output, strlen(output));
// Send file
fgets(output, sizeof(output), file);
while(!feof(file)) {
send(client, output, strlen(output), 0);
fgets(output, sizeof(output), file);
}
} else if (strcmp(fileType, "cgi") == 0) {
// Get possible arguments for histogram
char *array[5];
int numParam = getRequestParameters(buffer, array);
// Create a pipe to redirect the output
int pipe1[1];
pipe(pipe1);
// Fork the curent process to run the script
int pid = fork();
if (pid == 0) {
dup2(pipe1[1], 1);
close(pipe1[0]);
// Execute the script at filePath with the given parameters
if (numParam == 0) {
execl(filePath, filePath, NULL);
} else if (numParam == 2) {
execl(filePath, filePath, array[0], array[1], NULL);
} else if (numParam == 3) {
execl(filePath, filePath, array[0], array[1], array[2], NULL);
} else if (numParam == 4) {
execl(filePath, filePath, array[0], array[1], array[2], array[3], NULL);
} else if (numParam == 5) {
execl(filePath, filePath, array[0], array[1], array[2], array[3], array[4], NULL);
} else if (numParam == 6) {
execl(filePath, filePath, array[0], array[1], array[2], array[3], array[4], array[5], NULL);
}
exit(0);
} else {
close(pipe1[1]);
waitpid(pid, NULL, 0);
// Write the result
char buf;
while(read(pipe1[0], &buf, sizeof(buf)) > 0) {
write(client, &buf, 1);
}
close(pipe1[0]);
}
} else {
// Open the directory
DIR *directory = opendir(filePath);
if (directory != NULL) {
// Write the content type
sprintf(output, "Content-Type: text/plain:\n\nDirectory Listing:\n\n");
write(client, output, strlen(output));
// Traverse contents of directory
struct dirent *dent;
while((dent = readdir(directory)) != NULL) {
// Write current entry
sprintf(output, dent->d_name);
write(client, output, strlen(output));
write(client, "\n", 1);
}
// Close directory
closedir(directory);
} else {
// Print error if directory can't be found/ opened
throwHttpError(404, client);
}
}
close(client);
exit(0);
return 0;
}
int main(int argc, char *argv[]) {
int sockFd;
struct sockaddr_in serverAddr;
// Convert the port number to integer
int portNum = atoi(argv[1]);
// Open socket
if ((sockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "* ERROR: could not open socket\n");
exit(1);
}
// Set up socket address
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(portNum);
// Bind socket to server address
int size = sizeof(serverAddr);
if (bind(sockFd, (struct sockaddr *) &serverAddr, size) == -1) {
perror("* ERROR: could not bind\n");
exit(1);
}
// Listen for connections
if (listen(sockFd, 10) < 0) {
fprintf(stderr, "* ERROR: issues listening for a connection\n");
}
printf("* STATUS: web server is online!\n");
while(1) {
struct sockaddr_in clientAddr;
int size = sizeof(clientAddr);
// Get file descriptor
int clientFd;
if ((clientFd = accept(sockFd, (struct sockaddr *) &clientAddr, &size)) < 0) {
fprintf(stderr, "* ERROR: could not bind socket and get file descriptor\n");
continue;
}
// Create a child process
if (fork() == 0) {
close(sockFd);
// Execute the request
getRequest(clientFd);
close(clientFd);
exit(0);
}
close(clientFd);
}
}