Skip to content

Commit 68f6c60

Browse files
committed
HEAD method added
1 parent 27d6daf commit 68f6c60

4 files changed

Lines changed: 78 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- **HEAD method support** - Check resource existence without downloading content
12+
- `HEAD()` macro for defining HEAD routes
13+
- Example HEAD routes for `/`, `/test`, and static files
14+
- Returns HTTP headers without message body (RFC 7231 compliant)
1115
- Enhanced `/test` endpoint with comprehensive system information:
1216
- Current date/time with timezone
1317
- Server uptime calculation
@@ -32,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3236
- GitHub Actions CI/CD configuration
3337

3438
### Changed
39+
- Updated GitHub Actions workflow to use v4 of `actions/checkout` and `actions/upload-artifact`
3540
- Replaced `signal()` with `sigaction()` for more reliable signal handling
3641
- Replaced unsafe `sprintf()` with `snprintf()` throughout codebase
3742
- Fixed format string vulnerability in POST handler

README.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ A minimal HTTP server for Unix/Linux systems using fork-based concurrency. Simpl
1515

1616
## Features
1717

18-
- Simple routing with `GET()` and `POST()` macros
18+
- Simple routing with `GET()`, `POST()`, and `HEAD()` macros
1919
- Static file serving from `./public` directory
2020
- Request header parsing and access
2121
- POST payload handling
22+
- HEAD method support for checking resource existence
2223
- Fork-based concurrency (up to 1000 concurrent connections)
2324
- Path traversal protection
2425
- Graceful shutdown with signal handling
@@ -75,31 +76,41 @@ int main() {
7576
```c
7677
void route() {
7778
ROUTE_START()
78-
79+
7980
GET("/hello") {
8081
HTTP_200;
8182
printf("Hello, World!\n");
8283
}
83-
84+
8485
GET("/test") {
8586
HTTP_200;
8687
// Display system information
8788
printf("Server Uptime: ...\n");
8889
printf("OS: %s\n", ...);
8990
// Full implementation in main.c
9091
}
91-
92+
9293
POST("/data") {
9394
HTTP_201;
9495
printf("Received %d bytes\n", payload_size);
9596
// Access POST data via: payload, payload_size
9697
}
97-
98+
99+
HEAD("/file.pdf") {
100+
// Check if resource exists without sending body
101+
if (file_exists("public/file.pdf")) {
102+
HTTP_200;
103+
// Headers sent, no body (HEAD behavior)
104+
} else {
105+
HTTP_404;
106+
}
107+
}
108+
98109
GET(uri) {
99110
// Catch-all route for static files
100111
serve_static_file(uri);
101112
}
102-
113+
103114
ROUTE_END()
104115
}
105116
```
@@ -200,6 +211,7 @@ See `main.c` for a complete example. Key steps:
200211
- `ROUTE_START()` - Begin route definition
201212
- `GET(path)` - Define GET route
202213
- `POST(path)` - Define POST route
214+
- `HEAD(path)` - Define HEAD route (returns headers only, no body)
203215
- `ROUTE_END()` - End route definition
204216
205217
### Request Functions

httpd.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,28 @@ void route();
159159
*/
160160
#define POST(URI) ROUTE("POST", URI)
161161

162+
/**
163+
* @brief Define a HEAD route
164+
*
165+
* HEAD method is identical to GET except the server does not return
166+
* the message body in the response. Useful for checking if a resource
167+
* exists or getting metadata without downloading the full content.
168+
*
169+
* @param URI URI path to match (e.g., "/file.txt")
170+
*
171+
* @code
172+
* HEAD("/file.txt") {
173+
* if (file_exists("public/file.txt")) {
174+
* HTTP_200;
175+
* // Headers sent, no body
176+
* } else {
177+
* HTTP_404;
178+
* }
179+
* }
180+
* @endcode
181+
*/
182+
#define HEAD(URI) ROUTE("HEAD", URI)
183+
162184
/**
163185
* @brief End route definitions
164186
*

main.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,39 @@ void route() {
231231
}
232232
}
233233

234+
HEAD("/") {
235+
// HEAD is like GET but returns only headers, no body
236+
char index_html[256];
237+
if (build_public_path(index_html, sizeof(index_html), INDEX_HTML) == 0 &&
238+
file_exists(index_html)) {
239+
HTTP_200;
240+
// No body sent for HEAD requests
241+
} else {
242+
HTTP_200;
243+
// No body sent for HEAD requests
244+
}
245+
}
246+
247+
HEAD("/test") {
248+
// Return headers only, useful for checking if endpoint exists
249+
HTTP_200;
250+
// No body sent for HEAD requests
251+
}
252+
253+
HEAD(uri) {
254+
// Check if static file exists without sending content
255+
char file_name[256];
256+
if (build_public_path(file_name, sizeof(file_name), uri) == 0 &&
257+
is_path_safe(file_name, PUBLIC_DIR) &&
258+
file_exists(file_name)) {
259+
HTTP_200;
260+
// No body sent for HEAD requests
261+
} else {
262+
HTTP_404;
263+
// No body sent for HEAD requests
264+
}
265+
}
266+
234267
GET(uri) {
235268
serve_static_file(uri);
236269
}

0 commit comments

Comments
 (0)