-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathServer.c
329 lines (294 loc) · 8.02 KB
/
Server.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
322
323
324
325
326
327
328
329
/*
A command line based web server that takes http requests and responds to them aporopriately
it also supports sending files over a TCP connection
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdbool.h>
#define OPTSTRING "p:r:t:"
#define REQUIRED_ARGC 3
#define PORT_POS 1
#define MSG_POS 2
#define ERROR 1
#define QLEN 1
#define PROTOCOL "tcp"
#define BUFLEN 4098
#define SHORT 1024
bool pOption = false;
char *port;
bool rOption = false;
char *rootDirectory;
bool tOption = false;
char *authToken;
unsigned int addrlen;
int sd, sd2;
FILE *fileToSend;
char receivedtext[BUFLEN];
bool isGETCommand = false;
char *httpRequ;
char *filePath;
char *filePathToken;
char buffer[BUFLEN];
char pathForCommand[SHORT];
bool isMalformed = false;
bool badProtocol = false;
bool badMethod = false;
bool shuttingDown = false;
bool notAuthed = false;
bool invalidName = false;
bool okResponse = false;
bool notFound = false;
int errexit(char *format, char *arg)
{
fprintf(stderr, format, arg);
fprintf(stderr, "\n");
exit(ERROR);
}
void resetVariables()
{ //resets every error flag for each new request
isMalformed = false;
isGETCommand = false;
badProtocol = false;
badMethod = false;
shuttingDown = false;
notAuthed = false;
invalidName = false;
okResponse = false;
notFound = false;
}
void sendMessage(char *message)
{ /* write message to the connection */
if (write(sd2, message, strlen(message)) < 0)
errexit("error writing message: %s", message);
}
void sendFile(FILE *file)
{ // sends the whole file buffer by buffer throught the socket
int read1 = 0;
while (true)
{
memset(buffer, 0, BUFLEN);
read1 = fread(buffer, sizeof(char), BUFLEN, file);
if (write(sd2, buffer, read1) < 0)
errexit("error writing buffer: %s", buffer);
if (read1 < BUFLEN)
{
fclose(fileToSend);
break;
}
}
}
void checkRequest()
{//method to handle the request in the order specified
if (isMalformed)
{
sendMessage("HTTP/1.1 400 Malformed Request\r\n\r\n");
}
else if (badProtocol)
{
sendMessage("HTTP/1.1 501 Protocol Not Implemented\r\n\r\n");
}
else if (badMethod)
{
sendMessage("HTTP/1.1 405 Unsupported Method\r\n\r\n");
}
else if (shuttingDown)
{
sendMessage("HTTP/1.1 200 Server Shutting Down\r\n\r\n");
close(sd);
close(sd2);
exit(0);
}
else if (notAuthed)
{
sendMessage("HTTP/1.1 403 Operation Forbidden\r\n\r\n");
}
else if (invalidName)
{
sendMessage("HTTP/1.1 406 Invalid Filename\r\n\r\n");
}
else if (okResponse)
{
sendMessage("HTTP/1.1 200 OK\r\n\r\n");
sendFile(fileToSend);
}
else if (notFound)
{
sendMessage("HTTP/1.1 404 File Not Found\r\n\r\n");
}
}
void terminate(char *argument)
{//checks if the auth token is the same
if (strcmp(authToken, argument) == 0)
{
shuttingDown = true;
}
else
{
notAuthed = true;
}
}
void parseReceived(char *recvd)
{
char *CopyOfReceived = strdup(recvd);
//series of strtok to parse the commands, first is the method
char *commandptr = strtok(CopyOfReceived, " ");
if (strcmp(commandptr, "GET") == 0)
{
isGETCommand = true;
}
else if (strcmp(commandptr, "TERMINATE") == 0)
{
char *argument = strtok(NULL, " ");
terminate(argument);
}
else if (strcmp(commandptr, "TERMINATE") != 0 && strcmp(commandptr, "GET") != 0)
{
badMethod = true;
}
//second strtok gets the path
filePathToken = strtok(NULL, " ");
//stored elsewhere
filePath = strdup(filePathToken);
if (filePath[0] != '/')
{
invalidName = true;
}
if (strcmp(filePath, "/") == 0)
{
strcat(filePath, "homepage.html");
}
//third strtok is the HTTP version
httpRequ = strtok(NULL, " ");
char *httptext = strstr(recvd, "HTTP");
if (httptext == NULL)
{
badProtocol = true;
}
// makes sure the file ends in a double rnrn
char *endline = strstr(recvd, "\r\n\r\n");
if (endline == NULL)
{
isMalformed = true;
}
}
void openFile(char *filedir)
{//checks the file exists and can open
fileToSend = fopen(filedir, "rb");
if (fileToSend == NULL)
{
notFound = true;
}
else
{
okResponse = true;
}
}
void handleGetCommand()
{// opens a file when the get method is called correctly
if (isGETCommand)
{
memset(pathForCommand, 0, SHORT);
strcat(pathForCommand, rootDirectory);
strcat(pathForCommand, filePath);
openFile(pathForCommand);
}
}
void openPort(char port[])
{
struct sockaddr_in sin;
struct sockaddr addr;
struct protoent *protoinfo;
/* determine protocol */
if ((protoinfo = getprotobyname(PROTOCOL)) == NULL)
errexit("cannot find protocol information for %s", PROTOCOL);
/* setup endpoint info */
memset((char *)&sin, 0x0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons((u_short)atoi(port));
/* allocate a socket */
/* would be SOCK_DGRAM for UDP */
sd = socket(PF_INET, SOCK_STREAM, protoinfo->p_proto);
if (sd < 0)
errexit("cannot create socket", NULL);
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0)
errexit("setsockopt(SO_REUSEPORT) failed", port);
if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)) < 0)
errexit("setsockopt(SO_REUSEPORT) failed", port);
/* bind the socket */
if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
errexit("cannot bind to port %s", port);
/* listen for incoming connections */
if (listen(sd, QLEN) < 0)
errexit("cannot listen on port %s\n", port);
/* accept a connection */
addrlen = sizeof(addr);
int readError;
//loop to keep accepting HTTP requests serailly
while (readError >= 0)
{
memset(receivedtext, 0, sizeof(receivedtext));
sd2 = accept(sd, &addr, &addrlen);
if (sd2 < 0)
{
errexit("error accepting connection", NULL);
}
readError = read(sd2, receivedtext, sizeof(receivedtext));
if (readError < 0)
{
errexit("cannot read", NULL);
}
//parse the request
parseReceived(receivedtext);
// if it's a get command open a file
handleGetCommand();
//check if the request was correct/ file exists, return appropirate code or file
checkRequest();
//after this request is done, the flags must be reset
resetVariables();
close(sd2);
}
}
int main(int argc, char **argv)
{
char option;
while ((option = getopt(argc, argv, OPTSTRING)) != EOF)
{
switch (option)
{
case 'p':
pOption = true;
port = optarg;
break;
case 'r':
rOption = true;
rootDirectory = optarg;
break;
case 't':
tOption = true;
authToken = optarg;
break;
case '?':
errexit("correct usage ./proj3 -p port -r document_directory -t auth_token", NULL);
break;
case ':':
errexit("correct usage ./proj3 -p port -r document_directory -t auth_token", NULL);
break;
default:
break;
}
}
if (!pOption && !port)
errexit("correct usage ./proj3 -p port -r document_directory -t auth_token, -p missing ", NULL);
if (!rOption && !rootDirectory)
errexit("correct usage ./proj3 -p port -r document_directory -t auth_token, -r missing ", NULL);
if (!tOption && !authToken)
errexit("correct usage ./proj3 -p port -r document_directory -t auth_token, -t missing ", NULL);
openPort(port);
}