n_http_response: header validation (no response splitting), framing (Content-Length, Connection, Date), HEAD and bodiless statuses, memory and file bodies written through a write function that takes a few bytes at a time, single-range parsing
n_http_response: header validation (no response splitting), framing (Content-Length, Connection, Date), HEAD and bodiless statuses, memory and file bodies written through a write function that takes a few bytes at a time, single-range parsing
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
if (!ok) {
} else {
}
}
int opt = 0;
while ((opt = getopt(argc, argv, "V:")) != -1) {
if (opt == 'V') {
} else {
return -1;
}
}
return 0;
}
typedef struct {
size_t chunk;
long fail_at;
static ssize_t
sink_write(
void* ctx,
const void* buf,
size_t len) {
return -1;
return -1;
return (ssize_t)n;
}
char* date;
int res;
return 0;
date = strstr(
h->data,
"Date: ");
if (date) {
char* end = strstr(date, "\r\n");
memmove(date, end + 2, strlen(end + 2) + 1);
h->written = strlen(
h->data);
}
res = strcmp(
h->data, want) == 0;
if (!res)
return res;
}
if (!r)
return;
{
head_is(e,
"HTTP/1.1 200 OK\r\nX-Empty: \r\nContent-Length: 0\r\nConnection: close\r\n\r\n"));
}
"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nX-Trim: v\r\nset-cookie: c=3\r\n"
"Content-Length: 11\r\nConnection: close\r\n\r\n"));
"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nX-Trim: v\r\nset-cookie: c=3\r\n"
"Content-Length: 11\r\n\r\n"));
"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nX-Trim: v\r\nset-cookie: c=3\r\n"
"Content-Length: 11\r\nConnection: keep-alive\r\n\r\n"));
strstr(
h->data,
"Date: Thu, 01 Jan 1970 00:00:00 GMT\r\n") != NULL && strstr(
h->data + 20,
"Date:") == strstr(
h->data,
"Date:"));
{
int i, full = 1;
}
}
expect_true(
"304: no Content-Length",
head_is(r,
"HTTP/1.1 304 Not Modified\r\nETag: \"abc\"\r\n\r\n"));
{
sink s = {NULL, 1000, -1};
}
expect_true(
"204: no Content-Length",
head_is(r,
"HTTP/1.1 204 No Content\r\nETag: \"abc\"\r\n\r\n"));
expect_true(
"302 with its body length",
head_is(r,
"HTTP/1.1 302 Found\r\nETag: \"abc\"\r\nLocation: /x/\r\nContent-Length: 7\r\n\r\n"));
expect_true(
"empty body: Content-Length 0",
head_is(r,
"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"));
}
char path[] = "ex_http_response_XXXXXX";
int fd;
char data[200000];
size_t i;
{
}
{
sink s = {NULL, 1000, -1};
expect_true(
"HEAD writes the head only, with the full length",
}
{
}
for (i = 0; i < sizeof(data); i++)
data[i] = (char)('a' + i % 26);
fd = mkstemp(path);
if (fd >= 0) {
int fd2;
expect_true(
"temp file written", write(fd, data,
sizeof(data)) == (ssize_t)
sizeof(data));
close(fd);
fd2 = open(path, O_RDONLY);
{
sink s = {NULL, 4093, -1};
}
fd2 = open(path, O_RDONLY);
{
sink s = {NULL, 1000, -1};
}
fd2 = open(path, O_RDONLY);
{
sink s = {NULL, 1000, -1};
}
unlink(path);
}
}
long long a = -1, b = -1;
#define RANGE(s, total) n_http_parse_range((s), strlen(s), (total), &a, &b)
#undef RANGE
}
int main(
int argc,
char** argv) {
fprintf(stderr, "usage: %s [-V LOG_LEVEL]\n", argv[0]);
return 1;
}
return 1;
}
return 0;
}
void process_args(int argc, char **argv)
static void expect_true(const char *label, int cond)
static void test_headers(void)
headers: validation, set/remove, framing
static void test_status(void)
status codes and bodiless responses
static int head_is(const N_HTTP_RESP *r, const char *want)
text of the head of r, up to and excluding the Date line's value
long fail_at
fail once this many bytes were written, -1 never
static ssize_t sink_write(void *ctx, const void *buf, size_t len)
write function: accept at most s->chunk bytes
size_t chunk
most bytes accepted per call (partial writes)
static void test_write(void)
writing memory and file bodies, HEAD, partial and failing writes
static void test_range(void)
Range header parsing.
N_STR * data
everything written
an in-memory sink for n_http_response_write()
static int h(void *ctx, const N_ROUTE_MATCH *m)
a handler that does nothing (the test only routes)
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_NOTICE
normal but significant condition
#define LOG_INFO
informational
N_HTTP_RANGE n_http_parse_range(const char *value, size_t len, long long total, long long *first, long long *last)
interpret a single-range Range header for a body of total bytes
#define N_HTTP_RESPONSE_HEADERS_MAX
most headers a response may carry
int n_http_response_append(N_HTTP_RESP *r, const void *data, size_t len)
append bytes to the memory body (drops a file body)
const char * n_http_status_reason(int status)
reason phrase of a status code, a class phrase for an unlisted one
int n_http_response_head(const N_HTTP_RESP *r, N_STR **out)
append the status line and headers, through the blank line
int n_http_response_set_header(N_HTTP_RESP *r, const char *name, const char *value)
set a header: remove every header of that name, then add it
int n_http_response_add_header(N_HTTP_RESP *r, const char *name, const char *value)
add a header line after validating name and value
int n_http_response_file(N_HTTP_RESP *r, int fd, long long offset, long long length)
use a file range as the body; the response owns fd from now on
int n_http_response_set_status(N_HTTP_RESP *r, int status)
change the status code
long long n_http_response_body_length(const N_HTTP_RESP *r)
body length the response declares, 0 for a bodiless status
size_t n_http_response_remove_header(N_HTTP_RESP *r, const char *name)
remove every header of that name (ASCII case-insensitive)
int n_http_response_write(const N_HTTP_RESP *r, n_http_write_fn fn, void *ctx)
write the whole response (head, then body unless HEAD or bodiless)
int n_http_response_set_connection(N_HTTP_RESP *r, int keep_alive, int version_minor, int head_only)
connection handling for this response
void n_http_response_free(N_HTTP_RESP **r)
free a response (closing its file body) and set *r to NULL
const char * n_http_response_get_header(const N_HTTP_RESP *r, const char *name)
value of the first header of that name (ASCII case-insensitive)
N_HTTP_RESP * n_http_response_new(int status)
new response with a status, keep-alive off, HTTP/1.1
@ N_HTTP_RANGE_NONE
no usable Range: send the whole body (200)
@ N_HTTP_RANGE_UNSATISFIABLE
send 416, with a Content-Range that gives only the total
@ N_HTTP_RANGE_OK
one satisfiable range: send 206
a response under construction (fields are private, use the functions)
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
N_STR * nstrcat_ex(N_STR **dest, void *src, NSTRBYTE size, int resize_flag)
Append data into N_STR using internal N_STR size and cursor position.
A box including a string and his lenght.
Common headers and low-level functions & define.
HTTP/1.1 response: validated headers, memory or file body, framing.
N_STR and string function declaration.