-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttpserver.c
More file actions
205 lines (172 loc) · 5.59 KB
/
httpserver.c
File metadata and controls
205 lines (172 loc) · 5.59 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
/**
* HTTP Server Implementation
*
* This file contains the main entry point for an HTTP server that handles GET, PUT, and HEAD
* requests through a worker thread pool and request queue architecture.
*/
#include "io/bind.h"
#include "io/worker.h"
#include "api/queue.h"
#include "api/request_handle.h"
#include "api/constants.h"
#include "actions/read_file.h"
#include "actions/read_file_length.h"
#include "actions/write_file.h"
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
/* Number of worker threads to create */
#define WORKER_COUNT 1
/* Global variables for worker management */
Queue *request_queue; /* Shared request queue for all workers */
pthread_mutex_t request_poll_lock; /* Mutex to protect queue access */
Worker *worker[WORKER_COUNT]; /* Array of worker objects */
pthread_t worker_id[WORKER_COUNT]; /* Thread IDs for workers */
/**
* Prints help message for command-line usage
*/
static inline void print_help() {
fprintf(stderr, "usage: ./httpserver <port>\n");
}
/**
* Signal handler for clean termination
* Kills all worker threads and frees allocated resources
*
* @param code Signal code received
*/
void exitHandler(int code) {
// Kill and join all worker threads except the main one (index 0)
for (int i = 1; i < WORKER_COUNT; i++) {
Worker *W = worker[i];
if (W) {
pthread_kill(worker_id[i], SIGUSR1);
if (DEBUG) {
fprintf(stderr, "httpserver: killed worker %d\n", i);
}
pthread_join(worker_id[i], NULL);
if (DEBUG) {
fprintf(stderr, "httpserver: joined worker %d\n", i);
}
free(W);
worker[i] = NULL;
}
}
// Free the main worker
free(worker[0]);
// Free any remaining requests in the queue
if (request_queue) {
Completable *C;
while ((C = poll(request_queue)) != NULL) {
Request *R = (Request *) value(C);
free_request(R);
free(C);
}
free_queue(request_queue);
}
// Clean up response resources
cleanup_responses();
fprintf(stderr, "\nhttpserver: exiting: %d...\n", code);
exit(1);
}
/**
* Main entry point for the HTTP server
*
* @param argc Number of command line arguments
* @param argv Array of command line arguments
* @return Exit status code
*/
int main(int argc, char **argv) {
// Validate worker configuration
if (WORKER_COUNT < 1) {
fprintf(stderr, "httpserver: internal error: invalid WORKER_COUNT\n");
return 1;
}
// Check command line arguments
if (argc < 2) {
fprintf(stderr, "httpserver: not enough arguments\n");
print_help();
return 1;
}
// Parse port number from command line with validation
char *port = argv[1];
uint16_t parsed = 0;
char c;
int i = 0;
while ((c = port[i++]) != '\0') {
int d = c - '0';
if (d < 0 || d > 9) {
fprintf(stderr, "httpserver: invalid port digit: %d\n", d);
return 1;
}
uint16_t next = parsed * 10 + d;
// Check for overflow
if (next < parsed) {
fprintf(stderr, "httpserver: port too large\n");
return 1;
}
parsed = next;
}
// Double-check parsed port against standard library function
if (atoi(port) != parsed) {
fprintf(stderr, "httpserver: internal error: parsed port is incorrect: %d\n", parsed);
return 1;
}
// Initialize random number generator for potential use
time_t t;
srand((unsigned) time(&t));
// Create TCP socket and listen on specified port
int socket = create_listen_socket(parsed);
if (socket < 0) {
fprintf(stderr, "httpserver: internal error: failed to initialize server (%d)\n", socket);
return 1;
}
// Initialize epoll for I/O multiplexing
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
fprintf(stderr, "httpserver: internal error: failed to create epoll file descriptor\n");
return 1;
}
// Configure and register socket with epoll
struct epoll_event event = {0};
event.events = EPOLLIN; // Monitor for incoming connections
event.data.fd = socket;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket, &event)) {
fprintf(stderr, "httpserver: internal error: failed to add socket file descriptor to epoll\n");
close(epoll_fd);
return 1;
}
if (DEBUG) {
printf("httpserver: debug: epoll successfully bound to socket %d\n", socket);
}
// Register HTTP method handlers
handle_get = read_file;
handle_put = write_file;
handle_head = read_file_length;
// Create shared request queue
request_queue = create_queue();
// Initialize worker threads
for (int i = 0; i < WORKER_COUNT; i++) {
worker[i] = create_worker(epoll_fd, socket, &request_poll_lock, request_queue);
}
// Set up signal handler for clean termination
signal(SIGINT, exitHandler);
// Start worker threads (all except main thread at index 0)
for (int i = 1; i < WORKER_COUNT; i++) {
pthread_create(&worker_id[i], NULL, run, worker[i]);
}
// Use main thread as worker 0
worker_id[0] = pthread_self();
run(worker[0]);
// This point is never reached under normal execution
// as the main thread becomes a worker thread
return 0;
}