-
Notifications
You must be signed in to change notification settings - Fork 0
/
image_server.c
174 lines (150 loc) · 5.22 KB
/
image_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
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h> /* Internet domain header */
#include "socket.h"
#include "request.h"
#include "response.h"
#ifndef PORT
#define PORT 30000
#endif
#define BACKLOG 10
#define MAX_CLIENTS 10
/*
* Read data from a client socket, and, if there is enough information to
* determine the type of request, spawn a child process to respond to the
* request.
*
* Return 1 if one of the conditions hold:
* a) No bytes were read from the socket. (The client has likely closed the
* connection.)
* b) A child process has been created to respond to the request.
*
* This return value indicates that the server process should close the socket.
* Otherwise, return 0 (indicating that the server must continue to monitor the
* socket).
*
* Complete this function according to the comments within.
* Note that you'll be doing this incrementally, adding to the function as you
* complete the different parts of the assignment.
*/
int handle_client(ClientState *client) {
if (read_from_client(client) < 1) {
return 1;
}
if (parse_req_start_line(client) == 0) {
return 0;
}
// At this point client->reqData is not null, and so we are guaranteed
// to spawn a child process to handle the request (so we return 1).
// First, call fork. In the *parent* process, just return 1.
// In the *child* process, check the values in client->reqData to determine
// how to respond to the request.
pid_t result = fork();
if (result > 0) {
return 1;
}
//Child process responds to the request.
else if (result == 0) {
// Respond appropriately to a GET request.
if (strcmp(client->reqData->method, GET) == 0) {
if (strcmp(client->reqData->path, MAIN_HTML) == 0) {
main_html_response(client->sock);
} else if (strcmp(client->reqData->path, IMAGE_FILTER) == 0) {
image_filter_response(client->sock, client->reqData);
} else {
not_found_response(client->sock);
}
}
// Respond appropriately to a POST request.
else if (strcmp(client->reqData->method, POST) == 0) {
image_upload_response(client);
} else {
not_found_response(client->sock);
}
exit(0);
// Error checking in case the forking failed.
} else {
perror("Failed to fork.");
exit(1);
}
}
int main(int argc, char **argv) {
ClientState *clients = init_clients(MAX_CLIENTS);
struct sockaddr_in *servaddr = init_server_addr(PORT);
// Create an fd to listen to new connections.
int listenfd = setup_server_socket(servaddr, BACKLOG);
// Print out information about this server
char host[MAX_HOSTNAME];
if ((gethostname(host, sizeof(host))) == -1) {
perror("gethostname");
exit(1);
}
fprintf(stderr, "Server hostname: %s\n", host);
fprintf(stderr, "Port: %d\n", PORT);
// Set up the arguments for select
int maxfd = listenfd;
fd_set allset;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
// Set up a timer for select (This is only necessary for debugging help)
struct timeval timer;
// Main server loop.
while (1) {
fd_set rset = allset;
timer.tv_sec = 2;
timer.tv_usec = 0;
int nready = select(maxfd + 1, &rset, NULL, NULL, &timer);
if (nready == -1) {
perror("select");
exit(1);
}
if (nready == 0) { // timer expired
// Check if any children have failed
int status;
int pid;
errno = 0;
if ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
if (WIFSIGNALED(status)) {
fprintf(stderr, "Child [%d] failed with signal %d\n", pid,
WTERMSIG(status));
}
}
continue;
}
if (FD_ISSET(listenfd, &rset)) { // New client connection.
int new_client_fd = accept_connection(listenfd);
if (new_client_fd >= 0) {
maxfd = (new_client_fd > maxfd) ? new_client_fd : maxfd;
FD_SET(new_client_fd, &allset); // Add new descriptor to set.
for (int i = 0; i < MAX_CLIENTS; i++) {
if (clients[i].sock < 0) {
clients[i].sock = new_client_fd;
break;
}
}
}
nready -= 1;
}
// The nready is just an optimization; no harm in checking all fds
// except efficiency
for (int i = 0; i < MAX_CLIENTS && nready > 0; i++) {
// Check whether clients[i] has an active, ready socket.
if (clients[i].sock < 0 || !FD_ISSET(clients[i].sock, &rset)) {
continue;
}
int done = handle_client(&clients[i]);
if (done) {
FD_CLR(clients[i].sock, &allset);
remove_client(&clients[i]);
}
nready -= 1;
}
}
}