commit b76bde1b349f6246d29ce3dd767b97ed6d3f155b
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-05-28T13:49:32Z |
| subject | Refactor |
commit b76bde1b349f6246d29ce3dd767b97ed6d3f155b
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-05-28T13:49:32Z
Refactor
---
docs/tinyproxy.1.scd | 89 ++++++++++++++--
src/{udp_route.c => datagram_route.c} | 46 +++-----
src/datagram_route.h | 32 ++++++
src/endpoint.c | 44 ++++++++
src/endpoint.h | 40 +++++++
src/file_conf.c | 145 +++++++++++++++++--------
src/file_conf.h | 1 +
src/route.c | 40 +------
src/route.h | 35 +-----
src/route_runtime.c | 74 +++++++++++++
src/route_runtime.h | 27 +++++
src/{tcp_route.c => stream_route.c} | 33 +++---
src/stream_route.h | 21 ++++
src/tcp_route.h | 27 -----
src/tinyproxy.c | 195 ++++++++++++++++++++++------------
src/udp_route.h | 16 ---
src/worker.h | 11 ++
tests/support.py | 12 +--
tests/test_haproxy_proxy_v2.py | 7 +-
tests/test_tcp_backpressure.py | 14 +--
tests/test_tcp_connect_timeout.py | 14 +--
tests/test_tcp_idle_timeout.py | 16 +--
tests/test_tcp_keep_alive.py | 7 +-
tests/test_udp_idle_timeout.py | 9 +-
tests/test_udp_proxy_v2.py | 9 +-
tinyproxy.conf | 75 +++++++++----
26 files changed, 693 insertions(+), 346 deletions(-)
diff --git a/docs/tinyproxy.1.scd b/docs/tinyproxy.1.scd
index 6071c05..5c84b13 100644
--- a/docs/tinyproxy.1.scd
+++ b/docs/tinyproxy.1.scd
@@ -10,24 +10,97 @@ tinyproxy - lightweight TCP/UDP proxy
# DESCRIPTION
-tinyproxy forwards TCP and UDP traffic.
+tinyproxy forwards TCP and UDP traffic between endpoints.
-Supports:
+Supported endpoint types:
+- TCP
+- UDP
+- UNIX stream sockets
+- UNIX datagram sockets
+- file:// backends
+
+Features:
- PROXY protocol v2
-- UNIX sockets
-- UDP forwarding
+- idle and connect timeouts
+- keep-alive support
+- multiple routes
+- IPv4 and IPv6
+
+Routes may be configured using a config file or directly from the command line with *-L*.
# OPTIONS
*-c* <config>
- Load config file.
+ Load configuration file.
+
+*-L* <route>
+ Add a route using the same syntax as one config file line.
+ May be specified multiple times.
*-v*
- Show version.
+ Show version information.
+
+*-h*
+ Show help.
+
+# CONFIGURATION
+
+Each route uses the following syntax:
+
+ listen <listen-proto> <listen-endpoint> <backend-proto> <backend-endpoint> [options...]
+
+Supported protocols:
+- tcp
+- udp
+- unix
+- unix-dgram
+- file
+
+# CONFIGURATION EXAMPLES
+
+Forward TCP traffic:
+
+ listen tcp 0.0.0.0:80 tcp 10.0.1.1:80
+
+Enable PROXY protocol v2 and keep-alive:
+
+ listen tcp 0.0.0.0:443 tcp 10.0.1.1:443 proxy_v2 keep_alive
+
+Forward UDP traffic:
+
+ listen udp 0.0.0.0:5353 udp 10.0.1.1:53
+
+Forward to UNIX socket:
+
+ listen tcp 127.0.0.1:8080 unix /tmp/backend.sock
+
+Serve static HTTP response from file:
+
+ listen tcp 127.0.0.1:8080 file /tmp/response.http
+
+# COMMAND LINE EXAMPLES
+
+Run with config file:
+
+ tinyproxy -c /etc/tinyproxy.conf
+
+Define routes directly from the command line:
+
+ tinyproxy \
+ -L "tcp 0.0.0.0:80 tcp 10.0.1.1:80" \
+ -L "tcp 0.0.0.0:443 tcp 10.0.1.1:443 proxy_v2"
+
+# EXIT STATUS
+
+*0*
+ Success.
+
+*1*
+ Failure.
-# EXAMPLES
+# SEE ALSO
- tinyproxyc -c /etc/tinyproxy.conf
+ https://github.com/tgckpg/tinyproxy
# AUTHORS
diff --git a/src/udp_route.c b/src/datagram_route.c
similarity index 95%
rename from src/udp_route.c
rename to src/datagram_route.c
index ed67cb4..d05f6f7 100644
--- a/src/udp_route.c
+++ b/src/datagram_route.c
@@ -14,16 +14,17 @@
#include "compat_socket.h"
#include "compat_file.h"
#include "proxy_proto_v2.h"
+#include "worker.h"
#include "route.h"
-#include "udp_route.h"
+#include "datagram_route.h"
#include "x_builtins.h"
#define UDP_MAX_PACKET 65535
-struct udp_route_ctx;
+struct datagram_route_ctx;
struct udp_client {
- struct udp_route_ctx *ctx;
+ struct datagram_route_ctx *ctx;
evutil_socket_t fd;
struct event *ev;
@@ -38,20 +39,6 @@ struct udp_client {
struct udp_client *next;
};
-struct udp_route_ctx {
- struct event_base *base;
- struct worker *worker;
- const struct route *route;
-
- evutil_socket_t listen_fd;
- struct event *listen_ev;
-
- struct sockaddr_storage local_addr;
- socklen_t local_addr_len;
-
- struct udp_client *clients;
-};
-
static int sockaddr_equal(
const struct sockaddr_storage *a,
socklen_t a_len,
@@ -90,7 +77,7 @@ static void free_udp_client(struct udp_client *c)
free(c);
}
-static void cleanup_idle_udp_clients(struct udp_route_ctx *ctx)
+static void cleanup_idle_udp_clients(struct datagram_route_ctx *ctx)
{
time_t now = time(NULL);
struct udp_client **pp = &ctx->clients;
@@ -116,7 +103,7 @@ static void cleanup_idle_udp_clients(struct udp_route_ctx *ctx)
}
static struct udp_client *find_udp_client(
- struct udp_route_ctx *ctx,
+ struct datagram_route_ctx *ctx,
const struct sockaddr_storage *addr,
socklen_t addr_len
) {
@@ -134,7 +121,7 @@ static void upstream_read_cb(evutil_socket_t fd, short events, void *arg)
(void)events;
struct udp_client *c = arg;
- struct udp_route_ctx *ctx = c->ctx;
+ struct datagram_route_ctx *ctx = c->ctx;
unsigned char buf[UDP_MAX_PACKET];
@@ -394,7 +381,7 @@ static int connect_udp_upstream(struct udp_client *c, const struct endpoint *ups
}
static struct udp_client *create_udp_client(
- struct udp_route_ctx *ctx,
+ struct datagram_route_ctx *ctx,
const struct sockaddr_storage *client_addr,
socklen_t client_addr_len
) {
@@ -456,7 +443,7 @@ static int send_udp_payload_to_upstream(
const unsigned char *payload,
size_t payload_len
) {
- struct udp_route_ctx *ctx = c->ctx;
+ struct datagram_route_ctx *ctx = c->ctx;
const struct route *r = ctx->route;
if (!r->opts.proxy_v2) {
@@ -515,7 +502,7 @@ static void listen_read_cb(evutil_socket_t fd, short events, void *arg)
{
(void)events;
- struct udp_route_ctx *ctx = arg;
+ struct datagram_route_ctx *ctx = arg;
cleanup_idle_udp_clients(ctx);
@@ -594,10 +581,10 @@ static void listen_read_cb(evutil_socket_t fd, short events, void *arg)
}
}
-int start_udp_route(
+int start_datagram_route(
struct worker *w,
const struct route *r,
- struct udp_route_ctx **out
+ struct datagram_route_ctx *ctx
) {
if (r->listen.kind != ENDPOINT_INET) {
LOG_ERROR("unsupported udp listen endpoint",
@@ -640,7 +627,7 @@ int start_udp_route(
return -EINVAL;
}
- struct udp_route_ctx *ctx = calloc(1, sizeof(*ctx));
+ memset(ctx, 0, sizeof(*ctx));
if (ctx == NULL) {
return -ENOMEM;
}
@@ -657,7 +644,7 @@ int start_udp_route(
LOG_ERROR("udp listen socket failed",
"err", _LOGV(evutil_socket_error_to_string(err))
);
- free(ctx);
+ memset(ctx, 0, sizeof(*ctx));
return -errno;
}
@@ -745,11 +732,10 @@ int start_udp_route(
"options", _LOGV(opts[0] ? opts : "")
);
- *out = ctx;
return 0;
}
-void free_udp_route(struct udp_route_ctx *ctx)
+void stop_datagram_route(struct datagram_route_ctx *ctx)
{
if (ctx == NULL) {
return;
@@ -770,5 +756,5 @@ void free_udp_route(struct udp_route_ctx *ctx)
c = next;
}
- free(ctx);
+ memset(ctx, 0, sizeof(*ctx));
}
diff --git a/src/datagram_route.h b/src/datagram_route.h
new file mode 100644
index 0000000..b35d0d7
--- /dev/null
+++ b/src/datagram_route.h
@@ -0,0 +1,32 @@
+#ifndef DATAGRAM_ROUTE_H
+#define DATAGRAM_ROUTE_H
+
+#include <event2/util.h>
+#include "compat_socket.h"
+
+struct event;
+struct event_base;
+struct worker;
+struct route;
+struct udp_client;
+
+struct datagram_route_ctx {
+ struct event_base *base;
+ struct worker *worker;
+ const struct route *route;
+
+ evutil_socket_t listen_fd;
+ struct event *listen_ev;
+
+ struct sockaddr_storage local_addr;
+ socklen_t local_addr_len;
+
+ struct udp_client *clients;
+};
+
+int start_datagram_route(struct worker *w, const struct route *r,
+ struct datagram_route_ctx *ctx);
+
+void stop_datagram_route(struct datagram_route_ctx *ctx);
+
+#endif
diff --git a/src/endpoint.c b/src/endpoint.c
new file mode 100644
index 0000000..740936d
--- /dev/null
+++ b/src/endpoint.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <errno.h>
+
+#include "endpoint.h"
+
+int endpoint_to_string(const struct endpoint *ep, char *buf, size_t buf_len)
+{
+ if (buf == NULL || buf_len == 0) {
+ return -EINVAL;
+ }
+
+ if (ep == NULL) {
+ snprintf(buf, buf_len, "<nil>");
+ return 0;
+ }
+
+ switch (ep->kind) {
+ case ENDPOINT_INET:
+ snprintf(buf, buf_len, "%s:%u", ep->host, ep->port);
+ return 0;
+
+ case ENDPOINT_UNIX:
+ snprintf(buf, buf_len, "unix:%s", ep->path);
+ return 0;
+
+ case ENDPOINT_UNIX_DGRAM:
+ snprintf(buf, buf_len, "unix-dgram:%s", ep->path);
+ return 0;
+
+ case ENDPOINT_BUILTIN:
+ snprintf(buf, buf_len, "builtin://%s", x_builtin_name(ep->builtin));
+ return 0;
+
+ case ENDPOINT_FILE:
+ snprintf(buf, buf_len, "file://%s", ep->path);
+ return 0;
+
+ default:
+ snprintf(buf, buf_len, "<unknown>");
+ return 0;
+ }
+}
+
+
diff --git a/src/endpoint.h b/src/endpoint.h
new file mode 100644
index 0000000..8ffe14f
--- /dev/null
+++ b/src/endpoint.h
@@ -0,0 +1,40 @@
+#ifndef ENDPOINT_H
+#define ENDPOINT_H
+
+#include <stdint.h>
+
+#include "x_builtins.h"
+
+enum endpoint_proto {
+ PROTO_TCP,
+ PROTO_UDP,
+ PROTO_UNIX,
+ PROTO_UNIX_DGRAM,
+ PROTO_FILE,
+ PROTO_BUILTIN,
+};
+
+enum endpoint_kind {
+ ENDPOINT_INET,
+ ENDPOINT_UNIX,
+ ENDPOINT_UNIX_DGRAM,
+ ENDPOINT_BUILTIN,
+ ENDPOINT_FILE,
+};
+
+struct endpoint {
+ enum endpoint_kind kind;
+
+ char host[256];
+ uint16_t port;
+
+ enum endpoint_proto proto;
+
+ char path[108]; /* sockaddr_un sun_path limit on Linux */
+
+ enum x_builtin_upstream builtin;
+};
+
+int endpoint_to_string(const struct endpoint *ep, char *buf, size_t buf_len);
+
+#endif
diff --git a/src/file_conf.c b/src/file_conf.c
index 68126ea..776299c 100644
--- a/src/file_conf.c
+++ b/src/file_conf.c
@@ -8,8 +8,10 @@
#include "klog.h"
#include "file_conf.h"
+#include "endpoint.h"
#define MAX_LINE_LEN 512
+#define MAX_ROUTE_FIELDS 32
static char *trim(char *s)
{
@@ -76,7 +78,7 @@ static int split_host_port(const char *input,
return 0;
}
-static int parse_proto(const char *s, enum proto *out)
+static int parse_proto(const char *s, enum endpoint_proto *out)
{
if (strcmp(s, "tcp") == 0) {
*out = PROTO_TCP;
@@ -88,6 +90,26 @@ static int parse_proto(const char *s, enum proto *out)
return 0;
}
+ if (strcmp(s, "unix") == 0) {
+ *out = PROTO_UNIX;
+ return 0;
+ }
+
+ if (strcmp(s, "unix-dgram") == 0) {
+ *out = PROTO_UNIX_DGRAM;
+ return 0;
+ }
+
+ if (strcmp(s, "file") == 0) {
+ *out = PROTO_FILE;
+ return 0;
+ }
+
+ if (strcmp(s, "builtin") == 0) {
+ *out = PROTO_BUILTIN;
+ return 0;
+ }
+
return -1;
}
@@ -210,52 +232,68 @@ static const char *file_uri_path(const char *s)
return path;
}
-static int parse_endpoint(const char *s, struct endpoint *ep)
+static int parse_endpoint(enum endpoint_proto proto, const char *s, struct endpoint *ep)
{
const char *path;
memset(ep, 0, sizeof(*ep));
+ ep->proto = proto;
- if (strncmp(s, "unix:", 5) == 0) {
- path = s + 5;
+ switch (proto) {
+ case PROTO_TCP:
+ case PROTO_UDP:
+ ep->kind = ENDPOINT_INET;
- if (path[0] == '\0') {
+ if (split_host_port(s, ep->host, sizeof(ep->host), &ep->port) != 0) {
return -1;
}
- if (strlen(path) >= sizeof(ep->path)) {
+ return 0;
+
+ case PROTO_UNIX:
+ path = s;
+
+ if (strncmp(path, "unix:", 5) == 0) {
+ path += 5;
+ }
+
+ if (path[0] == '\0' || strlen(path) >= sizeof(ep->path)) {
return -1;
}
ep->kind = ENDPOINT_UNIX;
strcpy(ep->path, path);
return 0;
- } else if (strncmp(s, "builtin://", 10) == 0) {
- const char *name = s + 10;
- enum x_builtin_upstream builtin;
- if (name[0] == '\0') {
- return -1;
+ case PROTO_UNIX_DGRAM:
+ path = s;
+
+ if (strncmp(path, "unix-dgram:", 11) == 0) {
+ path += 11;
}
- if (x_builtin_parse(name, &builtin) < 0) {
+ if (path[0] == '\0' || strlen(path) >= sizeof(ep->path)) {
return -1;
}
- ep->kind = ENDPOINT_BUILTIN;
- ep->builtin = builtin;
-
+ ep->kind = ENDPOINT_UNIX_DGRAM;
+ strcpy(ep->path, path);
return 0;
- } else if (strncmp(s, "file://", 7) == 0) {
- const char *path = file_uri_path(s);
+
+ case PROTO_FILE:
+ if (strncmp(s, "file://", 7) == 0) {
+ path = file_uri_path(s);
+ } else {
+ path = s;
+ }
if (path[0] == '\0') {
- LOG_ERROR("missing file:// path");
+ LOG_ERROR("missing file path");
return -1;
}
if (strlen(path) >= sizeof(ep->path)) {
- LOG_ERROR("file:// path too long",
+ LOG_ERROR("file path too long",
"max", _LOGV(sizeof(ep->path) - 1)
);
return -1;
@@ -263,72 +301,95 @@ static int parse_endpoint(const char *s, struct endpoint *ep)
ep->kind = ENDPOINT_FILE;
strcpy(ep->path, path);
-
return 0;
- } else if (strncmp(s, "unix-dgram:", 11) == 0) {
- path = s + 11;
- if (path[0] == '\0') {
- return -1;
+ case PROTO_BUILTIN: {
+ enum x_builtin_upstream builtin;
+ const char *name = s;
+
+ if (strncmp(name, "builtin://", 10) == 0) {
+ name += 10;
}
- if (strlen(path) >= sizeof(ep->path)) {
+ if (name[0] == '\0' || x_builtin_parse(name, &builtin) < 0) {
return -1;
}
- ep->kind = ENDPOINT_UNIX_DGRAM;
- strcpy(ep->path, path);
+ ep->kind = ENDPOINT_BUILTIN;
+ ep->builtin = builtin;
return 0;
}
-
- ep->kind = ENDPOINT_INET;
-
- if (split_host_port(s, ep->host, sizeof(ep->host), &ep->port) != 0) {
- return -1;
}
- return 0;
+ return -1;
}
-static int parse_route_line(char *line, struct route *route)
+int parse_route_line(char *line, struct route *route)
{
- char *fields[4] = {0};
+ char *fields[MAX_ROUTE_FIELDS] = {0};
size_t count = 0;
+ size_t pos = 0;
char *saveptr = NULL;
char *tok;
+ enum endpoint_proto listen_proto;
+ enum endpoint_proto backend_proto;
tok = strtok_r(line, " \t\r\n", &saveptr);
while (tok != NULL) {
- if (count >= 4) {
- return -1; // too many fields
+ if (count >= MAX_ROUTE_FIELDS) {
+ return -1;
}
fields[count++] = tok;
tok = strtok_r(NULL, " \t\r\n", &saveptr);
}
- if (count < 3) {
+ if (count == 0) {
+ return -1;
+ }
+
+ /* Config file form:
+ * listen tcp 0.0.0.0:80 tcp 10.0.1.1:80 proxy_v2
+ *
+ * Also accept CLI-short form:
+ * tcp 0.0.0.0:80 tcp 10.0.1.1:80 proxy_v2
+ */
+ if (strcmp(fields[0], "listen") == 0) {
+ pos = 1;
+ }
+
+ if (count - pos < 4) {
return -1;
}
memset(route, 0, sizeof(*route));
route_options_set_defaults(&route->opts);
- if (parse_endpoint(fields[0], &route->listen) != 0) {
+ if (parse_proto(fields[pos], &listen_proto) != 0) {
return -1;
}
+ pos++;
- if (parse_endpoint(fields[1], &route->upstream) != 0) {
+ if (parse_endpoint(listen_proto, fields[pos], &route->listen) != 0) {
return -1;
}
+ pos++;
- if (parse_proto(fields[2], &route->proto) != 0) {
+ if (parse_proto(fields[pos], &backend_proto) != 0) {
return -1;
}
+ pos++;
- if (count == 4 && parse_route_options(fields[3], &route->opts) != 0) {
+ if (parse_endpoint(backend_proto, fields[pos], &route->upstream) != 0) {
return -1;
}
+ pos++;
+
+ for (; pos < count; pos++) {
+ if (parse_route_options(fields[pos], &route->opts) != 0) {
+ return -1;
+ }
+ }
return 0;
}
diff --git a/src/file_conf.h b/src/file_conf.h
index 2a76145..5b7baad 100644
--- a/src/file_conf.h
+++ b/src/file_conf.h
@@ -6,6 +6,7 @@
#include "route.h"
+int parse_route_line(char *line, struct route *route);
int load_routes_from_file(const char *path, struct route **routes_out, size_t *count_out);
void free_routes(struct route *routes);
diff --git a/src/route.c b/src/route.c
index 2efcdc6..aa368b9 100644
--- a/src/route.c
+++ b/src/route.c
@@ -1,44 +1,8 @@
#include <stdio.h>
#include <errno.h>
-#include "route.h"
-
-int endpoint_to_string(const struct endpoint *ep, char *buf, size_t buf_len)
-{
- if (buf == NULL || buf_len == 0) {
- return -EINVAL;
- }
-
- if (ep == NULL) {
- snprintf(buf, buf_len, "<nil>");
- return 0;
- }
-
- switch (ep->kind) {
- case ENDPOINT_INET:
- snprintf(buf, buf_len, "%s:%u", ep->host, ep->port);
- return 0;
- case ENDPOINT_UNIX:
- snprintf(buf, buf_len, "unix:%s", ep->path);
- return 0;
-
- case ENDPOINT_UNIX_DGRAM:
- snprintf(buf, buf_len, "unix-dgram:%s", ep->path);
- return 0;
-
- case ENDPOINT_BUILTIN:
- snprintf(buf, buf_len, "builtin://%s", x_builtin_name(ep->builtin));
- return 0;
-
- case ENDPOINT_FILE:
- snprintf(buf, buf_len, "file://%s", ep->path);
- return 0;
-
- default:
- snprintf(buf, buf_len, "<unknown>");
- return 0;
- }
-}
+#include "worker.h"
+#include "route.h"
void route_options_str(const struct route_options *opts, char *buf, size_t buflen)
{
diff --git a/src/route.h b/src/route.h
index 7e5c5e3..a8e7e99 100644
--- a/src/route.h
+++ b/src/route.h
@@ -5,19 +5,12 @@
#include <stddef.h>
#include <stdint.h>
-#include "x_builtins.h"
-
-struct event_base;
+#include "endpoint.h"
#define ROUTE_HOST_MAX 256
#define ROUTE_DEFAULT_IDLE_TIMEOUT_SEC 60
#define ROUTE_DEFAULT_CONNECT_TIMEOUT_SEC 5
-enum proto {
- PROTO_TCP,
- PROTO_UDP,
-};
-
struct route_options {
bool proxy_v2;
bool keep_alive;
@@ -26,41 +19,15 @@ struct route_options {
int connect_timeout_sec;
};
-enum endpoint_kind {
- ENDPOINT_INET,
- ENDPOINT_UNIX,
- ENDPOINT_UNIX_DGRAM,
- ENDPOINT_BUILTIN,
- ENDPOINT_FILE,
-};
-
-struct endpoint {
- enum endpoint_kind kind;
-
- char host[256];
- uint16_t port;
-
- char path[108]; /* sockaddr_un sun_path limit on Linux */
-
- enum x_builtin_upstream builtin;
-};
-
struct route {
struct endpoint listen;
struct endpoint upstream;
- enum proto proto;
struct route_options opts;
unsigned int line_no;
};
-struct worker {
- struct event_base *base;
- size_t id;
-};
-
-int endpoint_to_string(const struct endpoint *ep, char *buf, size_t buf_len);
void route_options_str(const struct route_options *opts, char *buf, size_t buflen);
#endif
diff --git a/src/route_runtime.c b/src/route_runtime.c
new file mode 100644
index 0000000..089457c
--- /dev/null
+++ b/src/route_runtime.c
@@ -0,0 +1,74 @@
+#include <string.h>
+
+#include "klog.h"
+#include "route.h"
+#include "route_runtime.h"
+
+int start_route(struct worker *w, const struct route *r, struct route_ctx *ctx)
+{
+ int rc = 0;
+ switch (r->listen.proto) {
+
+ case PROTO_TCP:
+ ctx->kind = ROUTE_CTX_STREAM;
+
+ rc = start_stream_route(w, r, &ctx->u.stream);
+ if (rc != 0) {
+ ctx->kind = ROUTE_CTX_NONE;
+ memset(&ctx->u.stream, 0, sizeof(ctx->u.stream));
+ }
+
+ return rc;
+
+ case PROTO_UDP:
+ ctx->kind = ROUTE_CTX_DATAGRAM;
+
+ rc = start_datagram_route(w, r, &ctx->u.datagram);
+ if (rc != 0) {
+ ctx->kind = ROUTE_CTX_NONE;
+ memset(&ctx->u.stream, 0, sizeof(ctx->u.datagram));
+ }
+
+ return rc;
+
+ /* New approach
+ case ENDPOINT_PROTO_TCP:
+ case ENDPOINT_PROTO_UNIX_STREAM:
+ return start_stream_listener(w, r, ctx);
+
+ case ENDPOINT_PROTO_UDP:
+ case ENDPOINT_PROTO_UNIX_DGRAM:
+ return start_datagram_listener(w, r, ctx);
+ */
+
+ default:
+ LOG_ERROR("route listen endpoint is not listenable",
+ "line", _LOGV(r->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen)
+ );
+ return -EINVAL;
+ }
+}
+
+void stop_route(struct route_ctx *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ switch (ctx->kind) {
+ case ROUTE_CTX_STREAM:
+ stop_stream_route(&ctx->u.stream);
+ break;
+
+ case ROUTE_CTX_DATAGRAM:
+ stop_datagram_route(&ctx->u.datagram);
+ break;
+
+ case ROUTE_CTX_NONE:
+ default:
+ break;
+ }
+
+ memset(ctx, 0, sizeof(*ctx));
+}
diff --git a/src/route_runtime.h b/src/route_runtime.h
new file mode 100644
index 0000000..fb8b359
--- /dev/null
+++ b/src/route_runtime.h
@@ -0,0 +1,27 @@
+#ifndef ROUTE_RUNTIME_H
+#define ROUTE_RUNTIME_H
+
+#include "route.h"
+#include "stream_route.h"
+#include "datagram_route.h"
+
+enum route_ctx_kind {
+ ROUTE_CTX_NONE,
+ ROUTE_CTX_STREAM,
+ ROUTE_CTX_DATAGRAM,
+};
+
+struct route_ctx {
+ enum route_ctx_kind kind;
+ const struct route *route;
+
+ union {
+ struct stream_route_ctx stream;
+ struct datagram_route_ctx datagram;
+ } u;
+};
+
+int start_route(struct worker *w, const struct route *r, struct route_ctx *ctx);
+void stop_route(struct route_ctx *ctx);
+
+#endif
diff --git a/src/tcp_route.c b/src/stream_route.c
similarity index 97%
rename from src/tcp_route.c
rename to src/stream_route.c
index ce46022..7ff1c84 100644
--- a/src/tcp_route.c
+++ b/src/stream_route.c
@@ -13,8 +13,9 @@
#include "file_conf.h"
#include "compat_socket.h"
#include "proxy_proto_v2.h"
+#include "worker.h"
#include "route.h"
-#include "tcp_route.h"
+#include "stream_route.h"
#include "x_builtins.h"
#define BEV_READ_HIGH_WATER (256 * 1024)
@@ -22,13 +23,6 @@
#define FILE_CHUNK_SIZE 4096
-struct tcp_route_ctx {
- struct event_base *accept_base;
- struct worker *worker;
- const struct route *route;
- struct evconnlistener *listener;
-};
-
typedef struct conn_s {
struct worker *owner;
const struct route *route;
@@ -637,7 +631,7 @@ static void accept_cb(
) {
(void)listener;
- struct tcp_route_ctx *ctx = arg;
+ struct stream_route_ctx *ctx = arg;
struct accepted_client ac = {
.fd = client_fd,
.route = ctx->route,
@@ -657,7 +651,7 @@ static void accept_cb(
static void accept_error_cb(struct evconnlistener *listener, void *arg)
{
- struct tcp_route_ctx *ctx = arg;
+ struct stream_route_ctx *ctx = arg;
int err = EVUTIL_SOCKET_ERROR();
LOG_ERROR("accept error", "err", _LOGV(evutil_socket_error_to_string(err)));
@@ -666,12 +660,15 @@ static void accept_error_cb(struct evconnlistener *listener, void *arg)
event_base_loopexit(ctx->accept_base, NULL);
}
-int start_tcp_route(
+int start_stream_route(
struct worker *w,
const struct route *r,
- struct tcp_route_ctx **out)
+ struct stream_route_ctx *ctx)
{
struct sockaddr_in listen_addr;
+
+ memset(ctx, 0, sizeof(*ctx));
+
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(r->listen.port);
@@ -683,11 +680,6 @@ int start_tcp_route(
return -EINVAL;
}
- struct tcp_route_ctx *ctx = calloc(1, sizeof(*ctx));
- if (ctx == NULL) {
- return -ENOMEM;
- }
-
ctx->accept_base = w->base;
ctx->worker = w;
ctx->route = r;
@@ -704,7 +696,7 @@ int start_tcp_route(
if (ctx->listener == NULL) {
LOG_ERROR("evconnlistener_new_bind failed");
- free(ctx);
+ memset(ctx, 0, sizeof(*ctx));
return -EADDRINUSE;
}
@@ -721,11 +713,10 @@ int start_tcp_route(
"options", _LOGV(opts[0] ? opts : "")
);
- *out = ctx;
return 0;
}
-void free_tcp_route(struct tcp_route_ctx *ctx)
+void stop_stream_route(struct stream_route_ctx *ctx)
{
if (ctx == NULL) {
return;
@@ -735,7 +726,7 @@ void free_tcp_route(struct tcp_route_ctx *ctx)
evconnlistener_free(ctx->listener);
}
- free(ctx);
+ memset(ctx, 0, sizeof(*ctx));
}
#ifdef FUZZ
diff --git a/src/stream_route.h b/src/stream_route.h
new file mode 100644
index 0000000..7ab549e
--- /dev/null
+++ b/src/stream_route.h
@@ -0,0 +1,21 @@
+#ifndef STREAM_ROUTE_H
+#define STREAM_ROUTE_H
+
+struct worker;
+struct route;
+struct event_base;
+struct evconnlistener;
+
+struct stream_route_ctx {
+ struct event_base *accept_base;
+ struct worker *worker;
+ const struct route *route;
+ struct evconnlistener *listener;
+};
+
+int start_stream_route(struct worker *w, const struct route *r,
+ struct stream_route_ctx *ctx);
+
+void stop_stream_route(struct stream_route_ctx *ctx);
+
+#endif
diff --git a/src/tcp_route.h b/src/tcp_route.h
deleted file mode 100644
index ec9b8b1..0000000
--- a/src/tcp_route.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef TCP_ROUTE_H
-#define TCP_ROUTE_H
-
-#include "route.h"
-#ifdef FUZZ
-#include "compat_socket.h"
-#endif
-
-struct tcp_route_ctx;
-
-int start_tcp_route(
- struct worker *w,
- const struct route *r,
- struct tcp_route_ctx **out);
-
-void free_tcp_route(struct tcp_route_ctx *ctx);
-#endif
-
-#ifdef FUZZ
-int tcp_route_adopt_client_for_fuzz(
- struct event_base *base,
- const struct route *r,
- evutil_socket_t client_fd,
- const struct sockaddr_storage *peer_addr,
- socklen_t peer_addr_len
-);
-#endif
diff --git a/src/tinyproxy.c b/src/tinyproxy.c
index 3f2e341..eb146e9 100644
--- a/src/tinyproxy.c
+++ b/src/tinyproxy.c
@@ -10,18 +10,21 @@
#include "klog.h"
#include "signals.h"
#include "file_conf.h"
-#include "tcp_route.h"
-#include "udp_route.h"
+#include "worker.h"
+#include "route.h"
+#include "route_runtime.h"
static void usage(FILE *out, const char *prog)
{
fprintf(out,
- "usage: %s [-c config-file]\n"
+ "usage: %s [-c config-file | -L route]\n"
"\n"
"options:\n"
" -c FILE path to config file (default: tinyproxy.conf)\n"
- " -v show the version\n"
- " -h show this help\n",
+ " -L ROUTE add inline route, same syntax as config line\n"
+ " may be specified multiple times\n"
+ " -v show the version\n"
+ " -h show this help\n",
prog);
}
@@ -48,6 +51,12 @@ int main(int argc, char **argv)
int exit_code = 1;
int rc;
+ int conf_path_set = 0;
+
+ const char **inline_routes = NULL;
+ size_t inline_route_count = 0;
+ size_t inline_route_cap = 0;
+
#ifdef _WIN32
int wsa_started = 0;
#endif
@@ -55,51 +64,120 @@ int main(int argc, char **argv)
struct route *routes = NULL;
size_t route_count = 0;
- struct tcp_route_ctx **tcp_ctxs = NULL;
- struct udp_route_ctx **udp_ctxs = NULL;
- size_t tcp_ctx_count = 0;
- size_t udp_ctx_count = 0;
+ struct route_ctx *route_ctxs = NULL;
+ size_t route_ctx_count = 0;
struct event_base *base = NULL;
struct signal_events signals;
int signals_started = 0;
- while ((opt = getopt(argc, argv, "c:h:v")) != -1) {
+ while ((opt = getopt(argc, argv, "c:L:h:v")) != -1) {
switch (opt) {
- case 'c':
- if (optarg == NULL || optarg[0] == '\0') {
- LOG_ERROR("missing config path after -c");
+ case 'c':
+ if (optarg == NULL || optarg[0] == '\0') {
+ LOG_ERROR("missing config path after -c");
+ free(inline_routes);
+ return 2;
+ }
+ conf_path = optarg;
+ conf_path_set = 1;
+ break;
+
+ case 'L':
+ if (optarg == NULL || optarg[0] == '\0') {
+ LOG_ERROR("missing route after -L");
+ free(inline_routes);
+ return 2;
+ }
+
+ if (inline_route_count == inline_route_cap) {
+ size_t new_cap = inline_route_cap == 0 ? 4 : inline_route_cap * 2;
+ const char **new_routes = realloc(inline_routes,
+ new_cap * sizeof(*inline_routes));
+
+ if (new_routes == NULL) {
+ LOG_ERROR("inline route realloc failed",
+ "err", _LOGV(strerror(errno)));
+ free(inline_routes);
+ return 1;
+ }
+
+ inline_routes = new_routes;
+ inline_route_cap = new_cap;
+ }
+
+ inline_routes[inline_route_count++] = optarg;
+ break;
+
+ case 'h':
+ usage(stdout, argv[0]);
+ free(inline_routes);
+ return 0;
+
+ case 'v':
+ fprintf(stdout, "tinyproxy v0.0.1\n");
+ free(inline_routes);
+ return 0;
+
+ default:
+ usage(stderr, argv[0]);
+ free(inline_routes);
return 2;
- }
- conf_path = optarg;
- break;
-
- case 'h':
- usage(stdout, argv[0]);
- return 0;
-
- case 'v':
- fprintf(stdout, "tinyproxy v0.0.1\n");
- return 0;
-
- default:
- usage(stderr, argv[0]);
- return 2;
}
}
+ if (conf_path_set && inline_route_count > 0) {
+ LOG_ERROR("-c and -L cannot be used together");
+ usage(stderr, argv[0]);
+ free(inline_routes);
+ return 2;
+ }
+
if (optind != argc) {
LOG_ERROR("unexpected argument", "argv", _LOGV(argv[optind]));
usage(stderr, argv[0]);
return 2;
}
- rc = load_routes_from_file(conf_path, &routes, &route_count);
- if (rc != 0) {
- LOG_ERROR("failed to load config",
- "path", _LOGV(conf_path),
- "err", _LOGV(strerror(-rc)));
- goto out;
+ if (inline_route_count > 0) {
+ routes = calloc(inline_route_count, sizeof(*routes));
+ if (routes == NULL) {
+ LOG_ERROR("route calloc failed", "err", _LOGV(strerror(errno)));
+ goto out;
+ }
+
+ for (size_t i = 0; i < inline_route_count; i++) {
+ char *line = strdup(inline_routes[i]);
+ if (line == NULL) {
+ LOG_ERROR("inline route strdup failed", "err", _LOGV(strerror(errno)));
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ struct route *r = &routes[route_count];
+ r->line_no = (unsigned int)i + 1;
+
+ rc = parse_route_line(line, r);
+ free(line);
+
+ if (rc != 0) {
+ LOG_ERROR("failed to load inline route",
+ "line", _LOGV(r->line_no),
+ "route", _LOGV(inline_routes[i]),
+ "err", _LOGV(strerror(-rc)));
+ goto out;
+ }
+
+ route_count++;
+ }
+ } else {
+ rc = load_routes_from_file(conf_path, &routes, &route_count);
+ if (rc != 0) {
+ LOG_ERROR("failed to load config",
+ "path", _LOGV(conf_path),
+ "err", _LOGV(strerror(-rc)));
+ goto out;
+ }
}
if (route_count == 0) {
@@ -107,15 +185,9 @@ int main(int argc, char **argv)
goto out;
}
- tcp_ctxs = calloc(route_count, sizeof(*tcp_ctxs));
- if (tcp_ctxs == NULL) {
- LOG_ERROR("tcp calloc failed", "err", _LOGV(strerror(errno)));
- goto out;
- }
-
- udp_ctxs = calloc(route_count, sizeof(*udp_ctxs));
- if (udp_ctxs == NULL) {
- LOG_ERROR("udp calloc failed", "err", _LOGV(strerror(errno)));
+ route_ctxs = calloc(route_count, sizeof(*route_ctxs));
+ if (route_ctxs == NULL) {
+ LOG_ERROR("route calloc failed", "err", _LOGV(strerror(errno)));
goto out;
}
@@ -149,33 +221,17 @@ int main(int argc, char **argv)
for (size_t i = 0; i < route_count; i++) {
const struct route *r = &routes[i];
- switch (r->proto) {
- case PROTO_TCP:
- rc = start_tcp_route(&w, r, &tcp_ctxs[tcp_ctx_count]);
- if (rc == 0) {
- tcp_ctx_count++;
- }
- break;
-
- case PROTO_UDP:
- rc = start_udp_route(&w, r, &udp_ctxs[udp_ctx_count]);
- if (rc == 0) {
- udp_ctx_count++;
- }
- break;
-
- default:
- LOG_ERROR("route has unknown protocol", "line", _LOGV(r->line_no));
- rc = -EINVAL;
- break;
+ rc = start_route(&w, r, &route_ctxs[route_ctx_count]);
+ if (rc == 0) {
+ route_ctx_count++;
}
if (rc != 0) {
LOG_ERROR("failed to start route",
- "line", _LOGV(r->line_no),
- "listen", _LOGV_ENDPOINT(&r->listen),
- "upstream", _LOGV_ENDPOINT(&r->upstream)
- );
+ "line", _LOGV(r->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
goto out;
}
}
@@ -189,21 +245,22 @@ int main(int argc, char **argv)
exit_code = 0;
out:
- for (size_t i = 0; i < tcp_ctx_count; i++) {
- free_tcp_route(tcp_ctxs[i]);
+ for (size_t i = 0; i < route_ctx_count; i++) {
+ stop_route(&route_ctxs[i]);
}
if (signals_started) {
free_signal_handlers(&signals);
}
- free(tcp_ctxs);
+ free(route_ctxs);
if (base != NULL) {
event_base_free(base);
}
free_routes(routes);
+ free(inline_routes);
#ifdef _WIN32
if (wsa_started) {
diff --git a/src/udp_route.h b/src/udp_route.h
deleted file mode 100644
index 2e11f67..0000000
--- a/src/udp_route.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef UDP_ROUTE_H
-#define UDP_ROUTE_H
-
-#include "route.h"
-
-struct udp_route_ctx;
-
-int start_udp_route(
- struct worker *w,
- const struct route *r,
- struct udp_route_ctx **out
-);
-
-void free_udp_route(struct udp_route_ctx *ctx);
-
-#endif
diff --git a/src/worker.h b/src/worker.h
new file mode 100644
index 0000000..53c28e6
--- /dev/null
+++ b/src/worker.h
@@ -0,0 +1,11 @@
+#ifndef WORKER_H
+#define WORKER_H
+
+struct event_base;
+
+struct worker {
+ struct event_base *base;
+ size_t id;
+};
+
+#endif
diff --git a/tests/support.py b/tests/support.py
index 8a1bd37..c617f7d 100644
--- a/tests/support.py
+++ b/tests/support.py
@@ -389,9 +389,9 @@ async def run_tinyproxy_with_conf(
@asynccontextmanager
async def run_default_tcp_tinyproxy(proxy_bin: str):
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"tcp\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{PROXY_PORT}"
+ f" tcp {LISTEN_HOST}:{BACKEND_PORT}\n"
)
async with run_echo_backend(LISTEN_HOST, BACKEND_PORT):
@@ -407,9 +407,9 @@ async def run_default_tcp_tinyproxy(proxy_bin: str):
@asynccontextmanager
async def run_default_udp_tinyproxy(proxy_bin: str):
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"udp\n"
+ f"listen"
+ f" udp {LISTEN_HOST}:{PROXY_PORT}"
+ f" udp {LISTEN_HOST}:{BACKEND_PORT}\n"
)
async with run_udp_echo_backend(LISTEN_HOST, BACKEND_PORT):
diff --git a/tests/test_haproxy_proxy_v2.py b/tests/test_haproxy_proxy_v2.py
index fab0dfa..110616b 100644
--- a/tests/test_haproxy_proxy_v2.py
+++ b/tests/test_haproxy_proxy_v2.py
@@ -65,9 +65,10 @@ backend echo_backend
proxy_v2_option = os.environ.get("TINYPROXY_PROXY_V2_OPTION", "proxy_v2")
tinyproxy_conf = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{HAPROXY_FRONTEND_PORT} "
- f"tcp {proxy_v2_option}\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{PROXY_PORT}"
+ f" tcp {LISTEN_HOST}:{HAPROXY_FRONTEND_PORT}"
+ f" {proxy_v2_option}\n"
)
try:
diff --git a/tests/test_tcp_backpressure.py b/tests/test_tcp_backpressure.py
index 5089172..454eb9f 100644
--- a/tests/test_tcp_backpressure.py
+++ b/tests/test_tcp_backpressure.py
@@ -75,9 +75,10 @@ async def test_tcp_backpressure_when_upstream_does_not_read() -> None:
raise SkipTest("TINYPROXY_BIN is not set")
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"tcp idle_timeout=30\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{PROXY_PORT}"
+ f" tcp {LISTEN_HOST}:{BACKEND_PORT}"
+ f" idle_timeout=30\n"
)
stop_backend = asyncio.Event()
@@ -138,9 +139,10 @@ async def test_tcp_backpressure_when_client_does_not_read() -> None:
raise SkipTest("TINYPROXY_BIN is not set")
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"tcp idle_timeout=30\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{PROXY_PORT}"
+ f" tcp {LISTEN_HOST}:{BACKEND_PORT}"
+ f" idle_timeout=30\n"
)
backend_result: asyncio.Future[tuple[str, int]] = (
diff --git a/tests/test_tcp_connect_timeout.py b/tests/test_tcp_connect_timeout.py
index c999256..7f28398 100644
--- a/tests/test_tcp_connect_timeout.py
+++ b/tests/test_tcp_connect_timeout.py
@@ -36,9 +36,10 @@ async def test_tcp_connect_timeout_closes_client_connection() -> None:
raise SkipTest("TINYPROXY_BIN is not set")
conf_text = (
- f"{LISTEN_HOST}:{CONNECT_TIMEOUT_PROXY_PORT} "
- f"{BLACKHOLE_HOST}:{BLACKHOLE_PORT} "
- f"tcp connect_timeout=1,idle_timeout=30\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{CONNECT_TIMEOUT_PROXY_PORT}"
+ f" tcp {BLACKHOLE_HOST}:{BLACKHOLE_PORT}"
+ f" connect_timeout=1,idle_timeout=30\n"
)
async with run_tinyproxy_with_conf(
@@ -95,9 +96,10 @@ async def test_tcp_connect_timeout_does_not_replace_idle_timeout_after_connect()
from .support import BACKEND_PORT
conf_text = (
- f"{LISTEN_HOST}:{CONNECT_TIMEOUT_PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"tcp connect_timeout=1,idle_timeout=2\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{CONNECT_TIMEOUT_PROXY_PORT}"
+ f" tcp {LISTEN_HOST}:{BACKEND_PORT}"
+ f" connect_timeout=1,idle_timeout=2\n"
)
backend_server = await asyncio.start_server(
diff --git a/tests/test_tcp_idle_timeout.py b/tests/test_tcp_idle_timeout.py
index fe3080f..c3dab6d 100644
--- a/tests/test_tcp_idle_timeout.py
+++ b/tests/test_tcp_idle_timeout.py
@@ -45,9 +45,10 @@ async def test_tcp_idle_timeout_closes_idle_connection() -> None:
raise SkipTest("TINYPROXY_BIN is not set")
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"tcp idle_timeout=1\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{PROXY_PORT}"
+ f" tcp {LISTEN_HOST}:{BACKEND_PORT}"
+ f" idle_timeout=1\n"
)
backend_server = await asyncio.start_server(
@@ -100,9 +101,10 @@ async def test_tcp_idle_timeout_keeps_active_connection_open() -> None:
raise SkipTest("TINYPROXY_BIN is not set")
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"tcp idle_timeout=2\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{PROXY_PORT}"
+ f" tcp {LISTEN_HOST}:{BACKEND_PORT}"
+ f" idle_timeout=2\n"
)
backend_server = await asyncio.start_server(
@@ -149,4 +151,4 @@ async def test_tcp_idle_timeout_keeps_active_connection_open() -> None:
TESTS = [
("test_tcp_idle_timeout_closes_idle_connection", test_tcp_idle_timeout_closes_idle_connection),
("test_tcp_idle_timeout_keeps_active_connection_open", test_tcp_idle_timeout_keeps_active_connection_open),
-]
\ No newline at end of file
+]
diff --git a/tests/test_tcp_keep_alive.py b/tests/test_tcp_keep_alive.py
index 4d2c5ac..ddffc7f 100644
--- a/tests/test_tcp_keep_alive.py
+++ b/tests/test_tcp_keep_alive.py
@@ -61,9 +61,10 @@ async def test_tcp_keep_alive_roundtrip() -> None:
raise SkipTest("TINYPROXY_BIN is not set")
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"tcp keep_alive,idle_timeout=5\n"
+ f"listen"
+ f" tcp {LISTEN_HOST}:{PROXY_PORT}"
+ f" tcp {LISTEN_HOST}:{BACKEND_PORT}"
+ f" keep_alive,idle_timeout=5\n"
)
backend_server = await asyncio.start_server(
diff --git a/tests/test_udp_idle_timeout.py b/tests/test_udp_idle_timeout.py
index 66d78fd..12d23b4 100644
--- a/tests/test_udp_idle_timeout.py
+++ b/tests/test_udp_idle_timeout.py
@@ -47,9 +47,10 @@ async def test_udp_idle_timeout_expires_client_but_route_still_works() -> None:
raise SkipTest("TINYPROXY_BIN is not set")
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{BACKEND_PORT} "
- f"udp idle_timeout=1\n"
+ f"listen"
+ f" udp {LISTEN_HOST}:{PROXY_PORT} "
+ f" udp {LISTEN_HOST}:{BACKEND_PORT} "
+ f" idle_timeout=1\n"
)
loop = asyncio.get_running_loop()
@@ -114,4 +115,4 @@ TESTS = [
"test_udp_idle_timeout_expires_client_but_route_still_works",
test_udp_idle_timeout_expires_client_but_route_still_works,
),
-]
\ No newline at end of file
+]
diff --git a/tests/test_udp_proxy_v2.py b/tests/test_udp_proxy_v2.py
index fd81ac2..2426189 100644
--- a/tests/test_udp_proxy_v2.py
+++ b/tests/test_udp_proxy_v2.py
@@ -19,9 +19,10 @@ async def test_tinyproxy_sends_proxy_v2_for_udp() -> None:
raise SkipTest("TINYPROXY_BIN is not set")
conf_text = (
- f"{LISTEN_HOST}:{PROXY_PORT} "
- f"{LISTEN_HOST}:{UDP_PROXY_V2_BACKEND_PORT} "
- f"udp proxy_v2\n"
+ f"listen"
+ f" udp {LISTEN_HOST}:{PROXY_PORT} "
+ f" udp {LISTEN_HOST}:{UDP_PROXY_V2_BACKEND_PORT} "
+ f" proxy_v2\n"
)
async with run_udp_proxy_v2_echo_backend(
@@ -79,4 +80,4 @@ async def test_tinyproxy_sends_proxy_v2_for_udp() -> None:
TESTS = [
("test_tinyproxy_sends_proxy_v2_for_udp", test_tinyproxy_sends_proxy_v2_for_udp),
-]
\ No newline at end of file
+]
diff --git a/tinyproxy.conf b/tinyproxy.conf
index 3112617..f944557 100644
--- a/tinyproxy.conf
+++ b/tinyproxy.conf
@@ -1,35 +1,66 @@
-# builtin://client_addr
+# Format:
+# listen <listen-proto> <listen-endpoint> <backend-proto> <backend-endpoint> [options...]
+#
+# Endpoint examples:
+# tcp :80
+# tcp 0.0.0.0:80
+# tcp 10.0.1.1:80
+# udp :19132
+# unix /tmp/test.sock
+# unix-dgram /tmp/testd.sock
+# builtin client_addr
+# file examples/http-response.txt
+#
+# Options:
+# proxy_v2
+# keep_alive
+# idle_timeout=<seconds>
+# connect_timeout=<seconds>
+#
+# builtin client_addr
# Returns the observed client address as plain text, then closes.
# Example response: 127.0.0.1:54321
#
-# builtin://discard
+# builtin discard
# TCP: reads and discards client data until the client closes or timeout expires.
# UDP: silently drops each datagram.
#
-# builtin://hang
+# builtin hang
# Accepts the connection/datagram and intentionally produces no response.
# TCP: the connection remains open until the client closes or the route idle timeout expires.
# UDP: this is effectively a silent drop.
#
-# builtin://close
+# builtin close
# Accepts the connection and immediately closes it without reading or writing.
# For UDP, this is equivalent to discard/drop because UDP has no connection to close.
-# listen upstream proto option
-:80 10.0.1.1:80 tcp
-:443 10.0.1.1:443 tcp proxy_v2,keep_alive
-:12990 unix:/tmp/test.sock tcp
-:12345 10.0.1.1:12345 udp idle_timeout=10,connect_timeout=17
-:19132 10.0.1.1:19132 udp proxy_v2
-:13000 unix-dgram:/tmp/testd.sock udp
-:12995 builtin://client_addr tcp
-:12996 builtin://hang tcp idle_timeout=3,connect_timeout=1
-:12997 builtin://discard tcp
-:12998 builtin://close tcp
-:12995 builtin://client_addr udp
-:12996 builtin://hang udp
-:12997 builtin://discard udp
-:12998 builtin://close udp
-
-:14000 file://examples/http-response.txt tcp
-:14001 file:///does/not/exist tcp
+
+# TCP stream forwarding.
+listen tcp :80 tcp 10.0.1.1:80
+listen tcp :443 tcp 10.0.1.1:443 proxy_v2,keep_alive
+
+# TCP listener to UNIX stream backend.
+listen tcp :12990 unix /tmp/test.sock
+
+# UDP datagram forwarding.
+listen udp :12345 udp 10.0.1.1:12345 idle_timeout=10,connect_timeout=17
+listen udp :19132 udp 10.0.1.1:19132
+
+# UDP listener to UNIX datagram backend.
+listen udp :13000 unix-dgram /tmp/testd.sock
+
+# Builtin TCP test backends.
+listen tcp :12995 builtin client_addr
+listen tcp :12996 builtin hang idle_timeout=3,connect_timeout=1
+listen tcp :12997 builtin discard
+listen tcp :12998 builtin close
+
+# Builtin UDP test backends.
+listen udp :12995 builtin client_addr
+listen udp :12996 builtin hang
+listen udp :12997 builtin discard
+listen udp :12998 builtin close
+
+# Static file TCP backends.
+listen tcp :14000 file examples/http-response.txt
+listen tcp :14001 file /does/not/exist