Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_http3.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
35#include "nilorea/n_http3.h"
36#include "nilorea/n_log.h"
37
38#include <stdio.h>
39#include <string.h>
40
42static int g_fails = 0;
44#define CHECK(cond) \
45 do { \
46 if (!(cond)) { \
47 n_log(LOG_ERR, "CHECK failed: %s (%s:%d)", #cond, __FILE__, __LINE__); \
48 g_fails++; \
49 } \
50 } while (0)
51
53static void test_parse_url(void) {
54 char host[128];
55 char port[16];
56 char path[256];
57
58 CHECK(n_http3_parse_url("https://example.com/a/b?c=d", host, sizeof(host), port, sizeof(port), path, sizeof(path)) == 0);
59 CHECK(strcmp(host, "example.com") == 0);
60 CHECK(strcmp(port, "443") == 0);
61 CHECK(strcmp(path, "/a/b?c=d") == 0);
62
63 CHECK(n_http3_parse_url("https://host.test:8443/", host, sizeof(host), port, sizeof(port), path, sizeof(path)) == 0);
64 CHECK(strcmp(host, "host.test") == 0);
65 CHECK(strcmp(port, "8443") == 0);
66 CHECK(strcmp(path, "/") == 0);
67
68 CHECK(n_http3_parse_url("https://[::1]:443/x", host, sizeof(host), port, sizeof(port), path, sizeof(path)) == 0);
69 CHECK(strcmp(host, "::1") == 0);
70 CHECK(strcmp(port, "443") == 0);
71
72 /* a bare host with no path defaults the path to "/" */
73 CHECK(n_http3_parse_url("https://only.host", host, sizeof(host), NULL, 0, path, sizeof(path)) == 0);
74 CHECK(strcmp(path, "/") == 0);
75
76 /* non-https and NULL are rejected */
77 CHECK(n_http3_parse_url("http://insecure/", host, sizeof(host), port, sizeof(port), path, sizeof(path)) == -1);
78 CHECK(n_http3_parse_url(NULL, host, sizeof(host), port, sizeof(port), path, sizeof(path)) == -1);
79}
80
82static void test_request_guards(void) {
84 /* a NULL out is rejected by both entry points */
85 CHECK(n_http3_request("GET", "https://x/", NULL, NULL, 0, 100, NULL) == -1);
86 CHECK(n_http3_request_ex("GET", "https://x/", NULL, NULL, 0, 100, N_HTTP3_REQ_ALLOW_ILLEGAL_HEADERS, NULL) == -1);
87 /* an empty URL is rejected identically through the _ex entry (n_http3_request delegates
88 to n_http3_request_ex, so the two agree); the response error is set */
89 memset(&r, 0, sizeof(r));
90 CHECK(n_http3_request_ex("GET", "", NULL, NULL, 0, 100, 0u, &r) == -1);
91 CHECK(r.error != NULL);
93}
94
95int main(int argc, char** argv) {
97
100
101 n_log(LOG_NOTICE, "n_http3_available() = %d", n_http3_available());
102
103 if (argc > 1) {
104 N_HTTP3_RESPONSE resp;
105 int rv = n_http3_get(argv[1], 15000, &resp);
106 if (rv == 0) {
107 n_log(LOG_NOTICE, "HTTP/3 %d, %zu body bytes", resp.status, resp.body_len);
108 if (resp.headers) {
109 printf("%s", resp.headers);
110 }
111 printf("\n[%zu body bytes]\n", resp.body_len);
112 } else {
113 n_log(LOG_ERR, "HTTP/3 request failed: %s", resp.error ? resp.error : "(unknown)");
114 if (!n_http3_available()) {
115 n_log(LOG_NOTICE, "rebuild the library with HAVE_HTTP3 (ngtcp2 + nghttp3 + OpenSSL QUIC) to enable HTTP/3");
116 }
117 }
119
120 /* a second "illegal" argument demonstrates the security-testing mode: send a
121 normally-forbidden transfer-encoding header on the HTTP/3 request (a compliant
122 endpoint rejects it) via n_http3_request_ex with N_HTTP3_REQ_ALLOW_ILLEGAL_HEADERS */
123 if (argc > 2 && strcmp(argv[2], "illegal") == 0) {
125 int rv2 = n_http3_request_ex("GET", argv[1], "transfer-encoding: chunked\r\n", NULL, 0, 15000, N_HTTP3_REQ_ALLOW_ILLEGAL_HEADERS, &r2);
126 n_log(LOG_NOTICE, "HTTP/3 + illegal transfer-encoding header -> rv=%d status=%d%s%s",
127 rv2, r2.status, r2.error ? " error=" : "", r2.error ? r2.error : "");
129 }
130 } else {
131 n_log(LOG_NOTICE, "pass an https URL to fetch it over HTTP/3, e.g. ex_http3 https://cloudflare-quic.com/ (add 'illegal' to also send a forbidden framing header)");
132 }
133
134 if (g_fails == 0) {
135 n_log(LOG_NOTICE, "ex_http3: ALL PARSE TESTS PASSED");
136 } else {
137 n_log(LOG_ERR, "ex_http3: %d CHECK(s) FAILED", g_fails);
138 }
139 return g_fails ? 1 : 0;
140}
int main(void)
static int g_fails
test counter
Definition ex_http3.c:42
static void test_parse_url(void)
exercise the pure URL parser (runs in every build)
Definition ex_http3.c:53
#define CHECK(cond)
tiny assert that keeps going and tallies failures
Definition ex_http3.c:44
static void test_request_guards(void)
exercise the request entry points' guards + delegation (deterministic, no network)
Definition ex_http3.c:82
char * port
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_ERR
error conditions
Definition n_log.h:76
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:121
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:80
char * headers
Response header block as joined "name: value\r\n" lines (lowercased names), or NULL.
Definition n_http3.h:71
int status
HTTP status code, or 0 if none was received.
Definition n_http3.h:70
size_t body_len
Number of valid bytes in body.
Definition n_http3.h:73
char * error
Failure reason (heap), or NULL on a completed exchange.
Definition n_http3.h:74
int n_http3_parse_url(const char *url, char *host, size_t hostsz, char *port, size_t portsz, char *path, size_t pathsz)
Split an https URL into host, port and path (pure; available in every build).
Definition n_http3.c:57
int n_http3_request_ex(const char *method, const char *url, const char *headers, const unsigned char *body, size_t body_len, int timeout_ms, unsigned flags, N_HTTP3_RESPONSE *out)
Perform one blocking HTTP/3 request, with request flags.
Definition n_http3.c:1123
int n_http3_get(const char *url, int timeout_ms, N_HTTP3_RESPONSE *out)
Convenience wrapper: HTTP/3 GET of url.
Definition n_http3.c:128
int n_http3_request(const char *method, const char *url, const char *headers, const unsigned char *body, size_t body_len, int timeout_ms, N_HTTP3_RESPONSE *out)
Perform one blocking HTTP/3 request over a fresh QUIC connection.
Definition n_http3.c:1108
int n_http3_available(void)
Report whether HTTP/3 support was compiled into the library.
Definition n_http3.c:1104
void n_http3_response_free(N_HTTP3_RESPONSE *r)
Free the heap buffers held by r and zero the struct (safe on NULL / zeroed).
Definition n_http3.c:43
#define N_HTTP3_REQ_ALLOW_ILLEGAL_HEADERS
n_http3_request_ex flag: send the caller's extra headers WITHOUT the built-in connection-specific / f...
Definition n_http3.h:66
Result of an HTTP/3 exchange.
Definition n_http3.h:69
Minimal blocking HTTP/3 (over QUIC) client.
Generic log system.