emme coverage


Directory: src/
File: src/router.c
Date: 2025-08-24 07:42:18
Exec Total Coverage
Lines: 0 127 0.0%
Functions: 0 4 0.0%
Branches: 0 72 0.0%

Line Branch Exec Source
1 /* router.c - Routing module for the web server (TLS version)
2 *
3 * This module examines the HTTP request (HttpRequest) and, based on the routes
4 * defined in the configuration (ServerConfig), decides whether to:
5 * - Serve a static file (using sendfile for zero-copy).
6 * - Forward the request to a backend (reverse proxy).
7 *
8 * In TLS mode, data sent to the client uses SSL_write() and data is read
9 * using SSL_read().
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <sys/sendfile.h>
18 #include <arpa/inet.h>
19 #include <poll.h>
20 #include <errno.h>
21 #include "http2_response.h"
22 #include "router.h"
23 #include "server.h"
24
25 /* serve_static_tls()
26 *
27 * If the HTTP request's path starts with a static route, constructs the full file path,
28 * opens the file, and sends it to the client using SSL_write() and sendfile() for zero-copy.
29 * If the file is not found, a 404 response is sent.
30 */
31 int serve_static_tls(HttpRequest *req, ServerConfig *config, SSL *ssl)
32 {
33 if (!req || !req->path)
34 return -1;
35
36 for (int i = 0; i < config->route_count; i++)
37 {
38 if (strcmp(config->routes[i].technology, "static") == 0)
39 {
40 size_t prefix_len = strlen(config->routes[i].path);
41 if (strlen(req->path) < prefix_len)
42 continue;
43 if (strncmp(req->path, config->routes[i].path, prefix_len) == 0)
44 {
45 char filepath[512];
46 const char *root = config->routes[i].document_root;
47 bool has_slash = (root[strlen(root) - 1] == '/');
48 int written = snprintf(filepath, sizeof(filepath),
49 has_slash ? "%s%s" : "%s/%s",
50 root,
51 req->path + prefix_len);
52 if ((size_t)written >= sizeof(filepath))
53 {
54 fprintf(stderr, "serve_static_tls: Path too long\n");
55 return -1;
56 }
57
58 int fd = open(filepath, O_RDONLY);
59 if (fd < 0)
60 {
61 const char *not_found = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
62 SSL_write(ssl, not_found, strlen(not_found));
63 return 0;
64 }
65 off_t filesize = lseek(fd, 0, SEEK_END);
66 lseek(fd, 0, SEEK_SET);
67 char header[256];
68 snprintf(header, sizeof(header), "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n\r\n", filesize);
69 SSL_write(ssl, header, strlen(header));
70
71 printf("Sending static file: %s\n", filepath);
72 char filebuf[BUFFER_SIZE];
73 ssize_t bytes;
74 while ((bytes = read(fd, filebuf, sizeof(filebuf))) > 0)
75 {
76 printf("Sending %zd bytes from file\n", bytes);
77 int sent = 0;
78 while (sent < bytes) {
79 int n = SSL_write(ssl, filebuf + sent, bytes - sent);
80 if (n <= 0) {
81 close(fd);
82 return -1;
83 }
84 sent += n;
85 }
86 }
87 close(fd);
88
89 return 0;
90 }
91 }
92 }
93 return -1;
94 }
95
96 /* proxy_bidirectional_tls()
97 *
98 * Implements a bidirectional forwarding loop between a TLS client and a backend server.
99 * Data from the client is read with SSL_read() and forwarded to the backend via write(),
100 * while data from the backend is read with read() and forwarded to the client using SSL_write().
101 */
102 int proxy_bidirectional_tls(SSL *ssl, int backend_fd)
103 {
104 int done = 0;
105 char buf[BUFFER_SIZE];
106 while (!done)
107 {
108 /* Read from backend (plain) */
109 int n = read(backend_fd, buf, sizeof(buf));
110 if (n > 0)
111 {
112 int sent = 0;
113 while (sent < n)
114 {
115 int s = SSL_write(ssl, buf + sent, n - sent);
116 if (s <= 0)
117 {
118 done = 1;
119 break;
120 }
121 sent += s;
122 }
123 }
124 else if (n < 0)
125 {
126 done = 1;
127 }
128 /* Read from client (TLS) */
129 n = SSL_read(ssl, buf, sizeof(buf));
130 if (n > 0)
131 {
132 int sent = 0;
133 while (sent < n)
134 {
135 int s = send(backend_fd, buf + sent, n - sent, 0);
136 if (s <= 0)
137 {
138 done = 1;
139 break;
140 }
141 sent += s;
142 }
143 }
144 else if (n < 0)
145 {
146 done = 1;
147 }
148 }
149 return 0;
150 }
151
152 /* proxy_request_tls()
153 *
154 * Forwards the entire HTTP request to a backend for reverse proxy functionality.
155 * Then activates proxy_bidirectional_tls() to forward data bidirectionally.
156 */
157 int proxy_request_tls(HttpRequest *req, char *raw_request, int req_len, ServerConfig *config, SSL *ssl)
158 {
159 for (int i = 0; i < config->route_count; i++)
160 {
161 if (strcmp(config->routes[i].technology, "reverse_proxy") == 0)
162 {
163 size_t prefix_len = strlen(config->routes[i].path);
164 if (strlen(req->path) < prefix_len)
165 continue;
166 if (strncmp(req->path, config->routes[i].path, prefix_len) == 0)
167 {
168 char ip[64];
169 int port;
170 if (sscanf(config->routes[i].backend, "%63[^:]:%d", ip, &port) != 2)
171 return -1;
172 int backend_fd = socket(AF_INET, SOCK_STREAM, 0);
173 if (backend_fd < 0)
174 return -1;
175 struct sockaddr_in backend_addr;
176 memset(&backend_addr, 0, sizeof(backend_addr));
177 backend_addr.sin_family = AF_INET;
178 backend_addr.sin_port = htons(port);
179 if (inet_pton(AF_INET, ip, &backend_addr.sin_addr) <= 0)
180 {
181 close(backend_fd);
182 return -1;
183 }
184 if (connect(backend_fd, (struct sockaddr *)&backend_addr, sizeof(backend_addr)) < 0)
185 {
186 close(backend_fd);
187 return -1;
188 }
189 int sent = 0;
190 while (sent < req_len)
191 {
192 int n = send(backend_fd, raw_request + sent, req_len - sent, 0);
193 if (n <= 0)
194 break;
195 sent += n;
196 }
197 proxy_bidirectional_tls(ssl, backend_fd);
198 close(backend_fd);
199 return 0;
200 }
201 }
202 }
203 return -1;
204 }
205
206 /* route_request_tls()
207 *
208 * Decides how to handle the request:
209 * - If the request path is "/", serves a default HTML page.
210 * - Otherwise, it first tries to serve the request as a static file.
211 * - If that fails, it attempts to forward the request to the backend via reverse proxy.
212 * Note: All communication with the client is via the SSL pointer.
213 */
214 int route_request_tls(HttpRequest *req, const char *raw, size_t raw_len, ServerConfig *config, SSL *ssl, Http2Response *h2resp)
215 {
216 if (h2resp)
217 {
218 snprintf(h2resp->body, sizeof(h2resp->body), "<html><body>Hello, HTTP/2!</body></html>");
219 h2resp->body_len = strlen(h2resp->body);
220 if (h2resp->body_len == 0) {
221 h2resp->body[0] = '\n'; // or ' '
222 h2resp->body[1] = '\0';
223 h2resp->body_len = 1;
224 }
225 h2resp->status_code = 200;
226 strcpy(h2resp->status_text, "OK");
227 strcpy(h2resp->content_type, "text/html");
228 h2resp->num_headers = 0;
229 char status_str[4];
230 snprintf(status_str, sizeof(status_str), "%d", h2resp->status_code);
231 h2resp->headers[h2resp->num_headers++] = MAKE_NV(":status", status_str);
232 h2resp->headers[h2resp->num_headers++] = MAKE_NV("content-type", h2resp->content_type);
233 char clen[32];
234 snprintf(clen, sizeof(clen), "%zu", h2resp->body_len);
235 h2resp->headers[h2resp->num_headers++] = MAKE_NV("content-length", clen);
236 // Add more headers as needed
237 return 0;
238 }
239
240 if (strcmp(req->path, "/") == 0)
241 {
242 const char *html_content = "<html><head><title>High Performance Web Server</title></head>"
243 "<body><h1>Welcome to High Performance Web Server</h1>"
244 "<p>This server is designed to outperform Nginx and Apache by utilizing "
245 "advanced I/O techniques, a modular architecture, and an efficient reverse proxy mechanism.</p>"
246 "</body></html>";
247 size_t content_length = strlen(html_content);
248 char header[256];
249 snprintf(header, sizeof(header), "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %zu\r\n\r\n", content_length);
250 SSL_write(ssl, header, strlen(header));
251 SSL_write(ssl, html_content, content_length);
252 return 0;
253 }
254
255 if (serve_static_tls(req, config, ssl) == 0)
256 return 0;
257 if (proxy_request_tls(req, (char *)raw, raw_len, config, ssl) == 0)
258 return 0;
259
260 const char *not_found = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
261 SSL_write(ssl, not_found, strlen(not_found));
262 return -1;
263 }
264