commit ffda9e00ba5cdb289068ddcd79b9740875d917ab
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-05-26T19:44:34Z |
| subject | Added keep_alive, connect_timeou, idle_timeout options |
commit ffda9e00ba5cdb289068ddcd79b9740875d917ab
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-05-26T19:44:34Z
Added keep_alive, connect_timeou, idle_timeout options
---
file_conf.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++--------
proxy_proto_v2.h | 2 +
route.c | 44 ++++++++++++++---
route.h | 25 ++++++++--
tcp_route.c | 4 +-
tinyproxy.conf | 4 +-
udp_route.c | 7 ++-
7 files changed, 192 insertions(+), 40 deletions(-)
diff --git a/file_conf.c b/file_conf.c
index 50a52b8..08dc0cd 100644
--- a/file_conf.c
+++ b/file_conf.c
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <ctype.h>
#include <errno.h>
+#include <limits.h>
#include "klog.h"
#include "file_conf.h"
@@ -90,19 +91,123 @@ static int parse_proto(const char *s, enum proto *out)
return -1;
}
+static int parse_positive_int(const char *s, int *out)
+{
+ char *end = NULL;
+ long v;
+
+ if (s == NULL || *s == '\0') {
+ return -EINVAL;
+ }
+
+ errno = 0;
+ v = strtol(s, &end, 10);
+ if (errno != 0 || end == s || *end != '\0') {
+ return -EINVAL;
+ }
+
+ if (v <= 0 || v > INT_MAX) {
+ return -ERANGE;
+ }
+
+ *out = (int)v;
+ return 0;
+}
+
+static int parse_route_options(char *s, struct route_options *opts)
+{
+ char *saveptr = NULL;
+ char *tok;
+
+ if (s == NULL || *s == '\0') {
+ return 0;
+ }
+
+#define PARSE_INT_OPT(name, field) do { \
+ if (strcmp(key, name) == 0) { \
+ int rc = parse_positive_int(value, &opts->field); \
+ if (rc != 0) { \
+ return rc; \
+ } \
+ goto next_option; \
+ } \
+ } while (0)
+
+#define PARSE_BOOL_OPT(name, field) do { \
+ if (strcmp(tok, name) == 0) { \
+ opts->field = true; \
+ goto next_option; \
+ } \
+ } while (0)
+
+ for (tok = strtok_r(s, ",", &saveptr);
+ tok != NULL;
+ tok = strtok_r(NULL, ",", &saveptr)) {
+ char *eq;
+
+ tok = trim(tok);
+ if (*tok == '\0') {
+ return -EINVAL;
+ }
+
+ eq = strchr(tok, '=');
+
+ if (eq != NULL) {
+ const char *key;
+ const char *value;
+
+ *eq = '\0';
+ key = trim(tok);
+ value = trim(eq + 1);
+
+ if (*key == '\0' || *value == '\0') {
+ return -EINVAL;
+ }
+
+ PARSE_INT_OPT("idle_timeout", idle_timeout_sec);
+ PARSE_INT_OPT("connect_timeout", connect_timeout_sec);
+
+ return -EINVAL;
+ }
+
+ PARSE_BOOL_OPT("proxy_v2", proxy_v2);
+ PARSE_BOOL_OPT("keep_alive", keep_alive);
+
+ return -EINVAL;
+
+next_option:
+ continue;
+ }
+
+#undef PARSE_BOOL_OPT
+#undef PARSE_INT_OPT
+
+ return 0;
+}
+
+static inline void route_options_set_defaults(struct route_options *opts)
+{
+ opts->proxy_v2 = false;
+ opts->keep_alive = false;
+ opts->idle_timeout_sec = ROUTE_DEFAULT_IDLE_TIMEOUT_SEC;
+ opts->connect_timeout_sec = ROUTE_DEFAULT_CONNECT_TIMEOUT_SEC;
+}
+
static int parse_route_line(char *line, struct route *route)
{
char *fields[4] = {0};
size_t count = 0;
+ char *saveptr = NULL;
+ char *tok;
- char *tok = strtok(line, " \t");
- while (tok && count < 4) {
- fields[count++] = tok;
- tok = strtok(NULL, " \t");
- }
+ tok = strtok_r(line, " \t\r\n", &saveptr);
+ while (tok != NULL) {
+ if (count >= 4) {
+ return -1; // too many fields
+ }
- if (tok != NULL) {
- return -1; // too many fields
+ fields[count++] = tok;
+ tok = strtok_r(NULL, " \t\r\n", &saveptr);
}
if (count < 3) {
@@ -110,16 +215,17 @@ static int parse_route_line(char *line, struct route *route)
}
memset(route, 0, sizeof(*route));
+ route_options_set_defaults(&route->opts);
if (split_host_port(fields[0],
- route->listen_host, sizeof(route->listen_host),
- &route->listen_port) != 0) {
+ route->listen_host, sizeof(route->listen_host),
+ &route->listen_port) != 0) {
return -1;
}
if (split_host_port(fields[1],
- route->upstream_host, sizeof(route->upstream_host),
- &route->upstream_port) != 0) {
+ route->upstream_host, sizeof(route->upstream_host),
+ &route->upstream_port) != 0) {
return -1;
}
@@ -127,14 +233,8 @@ static int parse_route_line(char *line, struct route *route)
return -1;
}
- route->send_proxy_v2 = false;
-
- if (count == 4) {
- if (strcmp(fields[3], "proxy_v2") != 0) {
- return -1;
- }
-
- route->send_proxy_v2 = true;
+ if (count == 4 && parse_route_options(fields[3], &route->opts) != 0) {
+ return -1;
}
return 0;
@@ -193,9 +293,15 @@ int load_routes_from_file(const char *path, struct route **routes_out, size_t *c
cap = new_cap;
}
+ char original[MAX_LINE_LEN];
+ snprintf(original, sizeof(original), "%s", line);
+
struct route *r = &routes[count];
if (parse_route_line(line, r) != 0) {
- LOG_ERROR("invalid route config", "path", _LOGV(path), "line", _LOGV(line_no));
+ LOG_ERROR("invalid route config",
+ "path", _LOGV(path),
+ "line", _LOGV(line_no),
+ "text", _LOGV(original));
fclose(fp);
free(routes);
return -EINVAL;
diff --git a/proxy_proto_v2.h b/proxy_proto_v2.h
index 03e3435..9a2e579 100644
--- a/proxy_proto_v2.h
+++ b/proxy_proto_v2.h
@@ -12,6 +12,8 @@
#define PP2_TRANS_STREAM 0x01
#define PP2_TRANS_DGRAM 0x02
+struct bufferevent;
+
int proxy_v2_build(
unsigned char *buf,
size_t buf_len,
diff --git a/route.c b/route.c
index eb39569..83a069c 100644
--- a/route.c
+++ b/route.c
@@ -1,23 +1,53 @@
#include <stdio.h>
#include "route.h"
-void route_options_str(const struct route *r, char *buf, size_t buflen)
+void route_options_str(const struct route_options *opts, char *buf, size_t buflen)
{
size_t n = 0;
+ if (buflen == 0) {
+ return;
+ }
+
buf[0] = '\0';
-#define ADD_OPT(name) do { \
- n += snprintf(buf + n, buflen - n, "%s%s", n == 0 ? "" : ", ", name); \
+#define ADD_FMT(fmt, ...) do { \
+ int written; \
if (n >= buflen) { \
buf[buflen - 1] = '\0'; \
return; \
} \
+ written = snprintf( \
+ buf + n, \
+ buflen - n, \
+ "%s" fmt, \
+ n == 0 ? "" : ", ", \
+ __VA_ARGS__ \
+ ); \
+ if (written < 0) { \
+ buf[buflen - 1] = '\0'; \
+ return; \
+ } \
+ if ((size_t)written >= buflen - n) { \
+ n = buflen - 1; \
+ buf[n] = '\0'; \
+ return; \
+ } \
+ n += (size_t)written; \
} while (0)
- if (r->send_proxy_v2) {
- ADD_OPT("proxy_v2");
- }
+#define ADD_BOOL(name, enabled) \
+ ADD_FMT("%s=%s", name, (enabled) ? "true" : "false")
+
+#define ADD_INT(name, value) \
+ ADD_FMT("%s=%d", name, value)
+
+ ADD_BOOL("proxy_v2", opts->proxy_v2);
+ ADD_INT("idle_timeout", opts->idle_timeout_sec);
+ ADD_INT("connect_timeout", opts->connect_timeout_sec);
+ ADD_BOOL("keep_alive", opts->keep_alive);
-#undef ADD_OPT
+#undef ADD_INT
+#undef ADD_BOOL
+#undef ADD_FMT
}
diff --git a/route.h b/route.h
index cbe0502..500dee9 100644
--- a/route.h
+++ b/route.h
@@ -1,23 +1,38 @@
#ifndef ROUTE_H
#define ROUTE_H
-#include <event2/bufferevent.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct event_base;
+
+#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;
+
+ int idle_timeout_sec;
+ int connect_timeout_sec;
+};
+
struct route {
- char listen_host[64];
+ char listen_host[ROUTE_HOST_MAX];
uint16_t listen_port;
- char upstream_host[64];
+ char upstream_host[ROUTE_HOST_MAX];
uint16_t upstream_port;
enum proto proto;
- bool send_proxy_v2;
+ struct route_options opts;
unsigned int line_no;
};
@@ -27,6 +42,6 @@ struct worker {
size_t id;
};
-void route_options_str(const struct route *r, char *buf, size_t buflen);
+void route_options_str(const struct route_options *opts, char *buf, size_t buflen);
#endif
diff --git a/tcp_route.c b/tcp_route.c
index 8f87227..c84184d 100644
--- a/tcp_route.c
+++ b/tcp_route.c
@@ -165,7 +165,7 @@ static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac)
return;
}
- if (r->send_proxy_v2) {
+ if (r->opts.proxy_v2) {
struct sockaddr_in local_addr;
socklen_t local_len = sizeof(local_addr);
@@ -295,7 +295,7 @@ int start_tcp_route(
char opts[128];
- route_options_str(r, opts, sizeof(opts));
+ route_options_str(&r->opts, opts, sizeof(opts));
LOG_INFO("route started",
"line", _LOGV(r->line_no),
diff --git a/tinyproxy.conf b/tinyproxy.conf
index c7e7307..f7a3620 100644
--- a/tinyproxy.conf
+++ b/tinyproxy.conf
@@ -1,5 +1,5 @@
# listen upstream proto option
:80 10.0.1.1:80 tcp
-:443 10.0.1.1:443 tcp proxy_v2
-:12345 10.0.1.1:12345 udp
+:443 10.0.1.1:443 tcp proxy_v2,keep_alive
+:12345 10.0.1.1:12345 udp idle_timeout=10,connect_timeout=17
:19132 10.0.1.1:19132 udp proxy_v2
diff --git a/udp_route.c b/udp_route.c
index 4a4fc6e..c3e6af6 100644
--- a/udp_route.c
+++ b/udp_route.c
@@ -15,7 +15,6 @@
#include "udp_route.h"
#define UDP_MAX_PACKET 65535
-#define UDP_CLIENT_IDLE_TIMEOUT_SEC 60
struct udp_route_ctx;
@@ -89,7 +88,7 @@ static void cleanup_idle_udp_clients(struct udp_route_ctx *ctx)
while (*pp != NULL) {
struct udp_client *c = *pp;
- if (now - c->last_seen <= UDP_CLIENT_IDLE_TIMEOUT_SEC) {
+ if (now - c->last_seen <= ctx->route->opts.idle_timeout_sec) {
pp = &c->next;
continue;
}
@@ -263,7 +262,7 @@ static int send_udp_payload_to_upstream(
struct udp_route_ctx *ctx = c->ctx;
const struct route *r = ctx->route;
- if (!r->send_proxy_v2) {
+ if (!r->opts.proxy_v2) {
ssize_t sent = send(c->fd, (const char *)payload, payload_len, 0);
if (sent < 0) {
return -EVUTIL_SOCKET_ERROR();
@@ -491,7 +490,7 @@ int start_udp_route(
char opts[128];
- route_options_str(r, opts, sizeof(opts));
+ route_options_str(&r->opts, opts, sizeof(opts));
LOG_INFO("udp route started",
"line", _LOGV(r->line_no),