-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.c
247 lines (195 loc) · 6.15 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
#include <stdio.h> // console input/output, perror
#include <stdlib.h> // exit
#include <string.h> // string manipulation
#include <netdb.h> // getnameinfo
#include <sys/socket.h> // socket APIs
#include <netinet/in.h> // sockaddr_in
#include <unistd.h> // open, close
#include <signal.h> // signal handling
#include <time.h> // time
#define SIZE 1024 // buffer size
#define PORT 2728 // port number
#define BACKLOG 10 // number of pending connections queue will hold
/**
* @brief Generates file URL based on route
* @param route requested route
* @param fileurl generated url
*/
void getFileURL(char *route, char *fileURL);
/**
* @brief Sets *MIME to the mime type of file
* @param file file URL
* @param mime mime type of file
*/
void getMimeType(char *file, char *mime);
/**
* @brief Handles SIGINT signal
*/
void handleSignal(int signal);
/**
* @brief Returns a string with the current time in HTTP response date format
* @param buf buffer to store the time string
* https://stackoverflow.com/questions/7548759/generate-a-date-string-in-http-response-date-format-in-c
*/
void getTimeString(char *buf);
int serverSocket;
int clientSocket;
char *request;
int main()
{
// register signal handler
signal(SIGINT, handleSignal);
// server internet socket address
struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET; // IPv4
serverAddress.sin_port = htons(PORT); // port number in network byte order (host-to-network short)
serverAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // localhost (host to network long)
// socket of type IPv4 using TCP protocol
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
// reuse address and port
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
// bind socket to address
if (bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)
{
printf("Error: The server is not bound to the address.\n");
return 1;
}
// listen for connections
if (listen(serverSocket, BACKLOG) < 0)
{
printf("Error: The server is not listening.\n");
return 1;
}
// get server address information
char hostBuffer[NI_MAXHOST], serviceBuffer[NI_MAXSERV];
int error = getnameinfo((struct sockaddr *)&serverAddress, sizeof(serverAddress), hostBuffer,
sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), 0);
if (error != 0)
{
printf("Error: %s\n", gai_strerror(error));
return 1;
}
printf("\nServer is listening on http://%s:%s/\n\n", hostBuffer, serviceBuffer);
while (1)
{
// buffer to store data (request)
request = (char *)malloc(SIZE * sizeof(char));
char method[10], route[100];
// accept connection and read data
clientSocket = accept(serverSocket, NULL, NULL);
read(clientSocket, request, SIZE);
// parse HTTP request
sscanf(request, "%s %s", method, route);
printf("%s %s", method, route);
free(request);
// only support GET method
if (strcmp(method, "GET") != 0)
{
const char response[] = "HTTP/1.1 400 Bad Request\r\n\n";
send(clientSocket, response, sizeof(response), 0);
}
else
{
char fileURL[100];
// generate file URL
getFileURL(route, fileURL);
// read file
FILE *file = fopen(fileURL, "r");
if (!file)
{
const char response[] = "HTTP/1.1 404 Not Found\r\n\n";
send(clientSocket, response, sizeof(response), 0);
}
else
{
// generate HTTP response header
char resHeader[SIZE];
// get current time
char timeBuf[100];
getTimeString(timeBuf);
// generate mime type from file URL
char mimeType[32];
getMimeType(fileURL, mimeType);
sprintf(resHeader, "HTTP/1.1 200 OK\r\nDate: %s\r\nContent-Type: %s\r\n\n", timeBuf, mimeType);
int headerSize = strlen(resHeader);
printf(" %s", mimeType);
// Calculate file size
fseek(file, 0, SEEK_END);
long fsize = ftell(file);
fseek(file, 0, SEEK_SET);
// Allocates memory for response buffer and copies response header and file contents to it
char *resBuffer = (char *)malloc(fsize + headerSize);
strcpy(resBuffer, resHeader);
// Starting position of file contents in response buffer
char *fileBuffer = resBuffer + headerSize;
fread(fileBuffer, fsize, 1, file);
send(clientSocket, resBuffer, fsize + headerSize, 0);
free(resBuffer);
fclose(file);
}
}
close(clientSocket);
printf("\n");
}
}
void getFileURL(char *route, char *fileURL)
{
// if route has parameters, remove them
char *question = strrchr(route, '?');
if (question)
*question = '\0';
// if route is empty, set it to index.html
if (route[strlen(route) - 1] == '/')
{
strcat(route, "index.html");
}
// get filename from route
strcpy(fileURL, "htdocs");
strcat(fileURL, route);
// if filename does not have an extension, set it to .html
const char *dot = strrchr(fileURL, '.');
if (!dot || dot == fileURL)
{
strcat(fileURL, ".html");
}
}
void getMimeType(char *file, char *mime)
{
// position in string with period character
const char *dot = strrchr(file, '.');
// if period not found, set mime type to text/html
if (dot == NULL)
strcpy(mime, "text/html");
else if (strcmp(dot, ".html") == 0)
strcpy(mime, "text/html");
else if (strcmp(dot, ".css") == 0)
strcpy(mime, "text/css");
else if (strcmp(dot, ".js") == 0)
strcpy(mime, "application/js");
else if (strcmp(dot, ".jpg") == 0)
strcpy(mime, "image/jpeg");
else if (strcmp(dot, ".png") == 0)
strcpy(mime, "image/png");
else if (strcmp(dot, ".gif") == 0)
strcpy(mime, "image/gif");
else
strcpy(mime, "text/html");
}
void handleSignal(int signal)
{
if (signal == SIGINT)
{
printf("\nShutting down server...\n");
close(clientSocket);
close(serverSocket);
if (request != NULL)
free(request);
exit(0);
}
}
void getTimeString(char *buf)
{
time_t now = time(0);
struct tm tm = *gmtime(&now);
strftime(buf, sizeof buf, "%a, %d %b %Y %H:%M:%S %Z", &tm);
}