-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.c
More file actions
255 lines (213 loc) · 7.88 KB
/
server.c
File metadata and controls
255 lines (213 loc) · 7.88 KB
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
// https://pubs.opengroup.org/onlinepubs/009604499/basedefs/netinet/in.h.html
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
// https://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/socket.h.html
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#define PORT 8080
#define BACKLOG 10
/*
* Helpers
*/
typedef struct http_request_headers {
char *accept;
char *host;
char *user_agent;
} http_request_headers;
typedef struct http_request {
char method[10];
char path[1024];
char protocol[20];
http_request_headers headers;
} http_request;
// Returns 0 if true
int starts_with(char *string, char *target) {
return strncmp(string, target, strlen(target));
}
// Returns 0 if successful
int parse_request(char request_buffer[10240], http_request *request_ptr) {
char *token;
token = strtok(request_buffer, "\n");
sscanf(token, "%s %s %s", request_ptr->method, request_ptr->path, request_ptr->protocol);
while (token != NULL) {
if (starts_with(token, "Host:") == 0) {
char *host = token + strlen("Host: ");
request_ptr->headers.host = host;
} else if (starts_with(token, "Accept:") == 0) {
char *accept = token + strlen("Accept: ");
request_ptr->headers.accept = accept;
} else if (starts_with(token, "User-Agent:") == 0) {
char *ua = token + strlen("User-Agent: ");
request_ptr->headers.user_agent = ua;
}
token = strtok(NULL, "\n");
}
return 0;
}
/*
* Main
*/
int main() {
/***** VARIABLES *****/
// Boolean to use for setting socket option values
int socket_option_value = 1;
// Server internet socket address
struct sockaddr_in server_address;
// Protocal, IPv4
server_address.sin_family = AF_INET;
// Port number, in "network byte order" (host-to-network short)
server_address.sin_port = htons(PORT);
// Internet address, in "network byte order" (host-to-network long). Use INADDR_LOOPBACK for localhost, use INADDR_ANY for any address
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
/***** PROGRAM START *****/
// Define a socket
int server_socket = socket(
// Domain (int), use AF_INET for communicating between processes on different hosts via IPV4
AF_INET,
// Communication type, use SOCK_STREAM for TCP, SOCK_DGRAM for UDP
SOCK_STREAM,
// Protocol (int), use 0 for IP (internet protocol)
0
);
// Exit if create socket failed
if (server_socket < 0) {
printf("Failed to create socket\n");
return 1;
}
// Set socket option (useful for socket reuse)
int set_socket_option_result = setsockopt(
// Socket, The socket to set options for
server_socket,
// Level, "Socket" level or "protocol" level
SOL_SOCKET,
// Option name, the name of the single option to set
SO_REUSEADDR,
// Option value (const void *), "const void *" means a pointer to some memory that should not be modified
&socket_option_value,
// Option length
sizeof(int)
);
// Exit if set socket option failed
if (set_socket_option_result < 0) {
printf("Failed to set socket option\n");
return 1;
}
int socket_bind_result = bind(
// Socket (descriptor)
server_socket,
// The address to bind the socket to (protocol, port and address
(struct sockaddr *)&server_address,
// Length of the address in bytes
sizeof(server_address)
);
// Exit if socket bind failed
if (socket_bind_result < 0) {
printf("Failed to bind socket\n");
return 1;
}
// Start the socket listening for incoming connections
int listen_result = listen(
// Socket to listen with
server_socket,
// Backlog (max connections, socket will get an error if it recieves a connection when the current num of connections is already at the max
BACKLOG
);
// Exit if socket fails to listen
if (listen_result < 0) {
printf("Socket failed to listen\n");
return 1;
}
// Infinte loop to handle client connections
while (1) {
// Extract the first connection request in the pending connections queue and return a file descriptor to the connected client socket
int connected_client_socket = accept(
// Socket, listening socket on the server that we've already created, bound, and listened with
server_socket,
// address, null pointer or a pointer to a sockaddr structure to hold the address of the connecting socket
NULL,
// address_len, pointer to a variable (socklen_t) to hold length of the supplied sockaddr structure supplied in arg2, or null pointer if none was provided
NULL
);
// Exit if accept failed
if (connected_client_socket < 0) {
printf("Failed to accept connection\n");
return 1;
}
// Read the request data
char request_buffer[10240];
int num_of_bytes_read = read(
// Client socket to read from
connected_client_socket,
// Buffer into which to write the read data
request_buffer,
// Size to read
10240
);
// Exit if read failed
if (num_of_bytes_read < 0) {
printf("Faild to read request data\n");
return 1;
}
http_request request_data;
int parse_request_result = parse_request(request_buffer, &request_data);
// printf("Path: %s\n", request_data.path);
// printf("Method: %s\n", request_data.method);
// printf("Protocol: %s\n", request_data.protocol);
// printf("Accept: %s\n", request_data.headers.accept);
// printf("User-Agent: %s\n", request_data.headers.user_agent);
// printf("Host: %s\n", request_data.headers.host);
/*
* Response start
*/
// Respond to client
// Limit response size to 10240 bytes
char response[10204];
// char *response = "HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\n\n";
char file_path[1024];
strcat(file_path, "./public_html");
strcat(file_path, request_data.path);
FILE *file = fopen(file_path, "r");
if (file == NULL) {
strcat(response, "HTTP/1.1 404 Not Found\nContent-Type: text/html; charset=UTF-8\n\n");
} else {
// response = "HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\n\n<h1>Found it</h1>";
char buffer[1024];
int num_elements_read = fread(buffer, sizeof(char), sizeof(buffer), file);
if (num_elements_read == 0) {
strcat(response, "HTTP/1.1 500 Server Error\nContent-Type: text/html; charset=UTF-8\n\n");
} else {
strcat(response, "HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\n\n");
printf("Bufer: \n%s\n", buffer);
strcat(response, buffer);
}
fclose(file);
memset(buffer, '\0', 1024);
}
int num_of_bytes_sent = send(
// When sending a response from the server we need to use the client socket rather than the server socket
connected_client_socket,
// message
response,
// message size
strlen(response),
// flags
0
);
memset(response, '\0', 10204);
memset(file_path, '\0', 1024);
if (num_of_bytes_sent < 0) {
printf("Failed to respond to client\n");
return 1;
}
/*
* Response end
*/
// Close the connected socket
close(connected_client_socket);
// Close the listening socket
// close(server_socket); TEMP - don't close server socket because we're constanly listening
}
return 0;
}