Skip to content

Commit 49de931

Browse files
author
Tom Hinchliff
committed
Added epoll.
1 parent 74634f7 commit 49de931

File tree

4 files changed

+134
-85
lines changed

4 files changed

+134
-85
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
*.o
22
http-webhook
33
.vscode
4-
vgcore*
4+
vgcore*
5+
demo.txt

.valgrind.supp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
epoll_ctl
3+
Memcheck:Param
4+
epoll_ctl(event)
5+
fun:syscall
6+
fun:epoll_ctl
7+
}

filter.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ typedef enum
1717
TEST_FILTER,
1818
POST_TEST,
1919
// DO NOT REMOVE THIS
20+
GITHUB_EXAMPLE,
2021
NUM_FILTERS,
2122
} FILTERS;
2223

@@ -48,7 +49,18 @@ static const REQUEST_FILTER filters[] = {
4849
.num_headers = 0,
4950
.action = not_found_action,
5051
},
51-
};
52+
[GITHUB_EXAMPLE] = {
53+
.method = POST,
54+
.path = "/",
55+
.host = NULL,
56+
.headers = {
57+
{"X-GitHub-Event", "push"},
58+
{"User-Agent", "GitHub-Hookshot/68e1230"},
59+
{"content-type", "application/x-www-form-urlencoded"},
60+
},
61+
.num_headers = 1,
62+
.action = ok_action,
63+
}};
5264

5365
void filter_request(int client_socket, HTTP_REQUEST req, HTTP_RESPONSE *res);
5466

main.c

Lines changed: 112 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <sys/types.h>
1616
#include <sys/stat.h>
1717
#include <sys/epoll.h>
18+
#include <sys/timerfd.h>
1819
#include <netinet/in.h>
1920
#include <arpa/inet.h>
2021

@@ -66,6 +67,13 @@ void signal_handler(int signal_number)
6667
*/
6768
int main()
6869
{
70+
// We are going to use epoll to handle events on the server socket and all
71+
// incoming client sockets.
72+
struct epoll_event ev, events[MAX_EVENTS];
73+
// Not required but just prevents valgrind warning :).
74+
memset(&ev.data, '\0', sizeof(epoll_data_t));
75+
int epollfd, num_fds;
76+
6977
// Create a socket, using ipv4 and tcp.
7078
int yes = 1;
7179
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
@@ -82,10 +90,14 @@ int main()
8290
// Show the address we are listening on.
8391
show_server_address(&address);
8492

85-
/**
86-
* Register signal handler, we can do this now since we are prepared
87-
* to exit gracefully.
88-
*/
93+
// Initialize the epoll struct and register the server socket.
94+
epollfd = epoll_create1(0);
95+
ev.events = EPOLLIN;
96+
ev.data.fd = server_socket;
97+
epoll_ctl(epollfd, EPOLL_CTL_ADD, server_socket, &ev);
98+
99+
// Register signal handler, we can do this now since we are prepared
100+
// to exit gracefully.
89101
struct sigaction sig_int;
90102
memset(&sig_int, 0, sizeof(sig_int));
91103
sig_int.sa_handler = signal_handler;
@@ -96,76 +108,107 @@ int main()
96108

97109
while (running)
98110
{
99-
// Accept any incoming connections.
100-
struct sockaddr_in client_address;
101-
socklen_t sock_size = sizeof(struct sockaddr_in);
102-
client_socket = accept(server_socket,
103-
(struct sockaddr *)&client_address, &sock_size);
104-
105-
printf("------ Connection established ------\n");
106-
107-
// Read incoming connection's ip address.
108-
display_peer_ip((struct sockaddr *)&client_address.sin_addr);
109-
110-
// We need to keep reading from this socket until we encounter an
111-
// error, timeout or "Connection: close"
112-
bool connection = true;
113-
while (connection)
111+
// Wait for an incoming connection on the server socket.
112+
num_fds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
113+
// Encountered error waiting for event.
114+
if (num_fds == -1)
115+
perror("epoll_wait");
116+
117+
// Iterate over the returned number of fds
118+
for (int n = 0; n < num_fds; n++)
114119
{
115-
// Read and validate data if any is avaliable.
116-
HTTP_REQUEST request = {
117-
.version = UNSUPPORTED,
118-
.method = INV_METHOD,
119-
.path = NULL,
120-
.headers = NULL,
121-
.header_number = 0,
122-
.body = NULL,
123-
};
124-
125-
READ_STATES read_state = read_single_http_message(client_socket,
126-
&request);
127-
switch (read_state)
120+
// Are we handling events from the server socket? (Connections?)
121+
if (events[n].data.fd == server_socket)
128122
{
129-
case INVALID_DATA_READ:
130-
case MANUALLY_INTERRUPTED_READ:
131-
case UNEXPECTED_CLOSED_READ:
132-
printf("Error encountered reading packet: %s\n",
133-
READ_STATES_STRING[read_state]);
134-
connection = false;
135-
if (request.path != NULL)
136-
free(request.path);
137-
continue;
138-
case FINISHED_READ:
139-
// Show the breakdown of the http request.
140-
show_http_request(&request);
141-
// Filter data and action if required.
142-
HTTP_RESPONSE res;
143-
filter_request(client_socket, request, &res);
144-
break;
145-
case NO_DATA_READ:
146-
connection = false;
147-
break;
148-
default:
149-
break;
123+
struct sockaddr_in client_address;
124+
socklen_t sock_size = sizeof(struct sockaddr_in);
125+
client_socket = accept(server_socket,
126+
(struct sockaddr *)&client_address,
127+
&sock_size);
128+
if (client_socket == -1)
129+
{
130+
perror("Error accepting the connection.");
131+
running = false;
132+
continue;
133+
}
134+
135+
// Set socket as non blocking.
136+
fcntl(client_socket, F_SETFL,
137+
fcntl(client_socket, F_GETFL) | O_NONBLOCK);
138+
139+
// Add socket to epoll.
140+
ev.events = EPOLLIN | EPOLLET;
141+
ev.data.fd = client_socket;
142+
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client_socket,
143+
&ev) == -1)
144+
{
145+
perror("epoll_ctl: client_socket");
146+
}
147+
148+
printf("------ Connection established ------\n");
149+
150+
// Read incoming connection's ip address.
151+
display_peer_ip((struct sockaddr *)&client_address.sin_addr);
150152
}
151-
152-
// Are we keep-alive?
153-
const char *keep_alive = get_http_header_value(request,
154-
"Connection");
155-
if (keep_alive == NULL || strcmp(keep_alive, "keep-alive") != 0)
153+
else
156154
{
157-
connection = false;
155+
// Only other sockets are clients.
156+
// Are we reading or disconnecting?
157+
int connection = true;
158+
159+
// Read and validate data if any is avaliable.
160+
HTTP_REQUEST request = {
161+
.version = UNSUPPORTED,
162+
.method = INV_METHOD,
163+
.path = NULL,
164+
.headers = NULL,
165+
.header_number = 0,
166+
.body = NULL,
167+
};
168+
169+
READ_STATES read_state = read_single_http_message(client_socket,
170+
&request);
171+
switch (read_state)
172+
{
173+
case INVALID_DATA_READ:
174+
case MANUALLY_INTERRUPTED_READ:
175+
case UNEXPECTED_CLOSED_READ:
176+
case NO_DATA_READ:
177+
printf("Error encountered reading packet: %s\n",
178+
READ_STATES_STRING[read_state]);
179+
connection = false;
180+
if (request.path != NULL)
181+
free(request.path);
182+
break;
183+
case FINISHED_READ:
184+
// Show the breakdown of the http request.
185+
show_http_request(&request);
186+
// Filter data and action if required.
187+
HTTP_RESPONSE res;
188+
filter_request(client_socket, request, &res);
189+
// Are we keep-alive?
190+
const char *keep_alive = get_http_header_value(request,
191+
"Connection");
192+
if (keep_alive == NULL || strcmp(keep_alive, "keep-alive") != 0)
193+
{
194+
connection = false;
195+
}
196+
break;
197+
default:
198+
break;
199+
}
200+
201+
if (connection == false)
202+
{
203+
close(client_socket);
204+
client_socket = -1;
205+
printf("--------- Connection closed --------\n");
206+
}
207+
208+
// Free data.
209+
free_http_request(&request);
158210
}
159-
160-
// Free data.
161-
free_http_request(&request);
162211
}
163-
164-
// Close the connection.
165-
close(client_socket);
166-
client_socket = -1;
167-
168-
printf("--------- Connection closed --------\n");
169212
}
170213

171214
printf("Exiting now...\n");
@@ -179,21 +222,7 @@ int main()
179222
*/
180223
READ_STATES read_single_http_message(int s, HTTP_REQUEST *req)
181224
{
182-
// Use select to prevent recv from blocking until there is
183-
// something to read.
184-
sigset_t mask;
185-
sigset_t other_mask;
186-
sigemptyset(&mask);
187-
sigaddset(&mask, SIGINT);
188-
sigprocmask(1, &mask, &other_mask);
189-
fd_set fds;
190-
FD_ZERO(&fds);
191-
FD_SET(s, &fds);
192-
struct timespec timeout = {.tv_nsec = 3000};
193-
pselect(s + 1, &fds, NULL, NULL, &timeout, NULL);
194-
fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
195-
196-
// Actually read from the socket into the temporary buffer.
225+
// Read from the socket into the temporary buffer.
197226
char temp_buff[DEFAULT_PAYLOAD_SIZE];
198227
ssize_t bytes_read = 0;
199228

0 commit comments

Comments
 (0)