commit 45fe164d2fc9f7155fb4e2965f700c1790021327
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-05-28T19:06:01Z |
| subject | Refactor some more |
commit 45fe164d2fc9f7155fb4e2965f700c1790021327
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-05-28T19:06:01Z
Refactor some more
---
devtools/cflow.sh | 64 +++++++++++++++++++++++++++++
src/datagram_route.c | 112 +++++++++++++++++++++++++++++++--------------------
src/endpoint.c | 36 +++++++++++++++++
src/endpoint.h | 6 ++-
src/file_conf.c | 4 +-
src/route_runtime.c | 82 +++++++++++++++++++++++++++----------
src/route_runtime.h | 2 +
src/stream_route.c | 55 +++++++++++++++++--------
src/tinyproxy.c | 10 +++++
9 files changed, 286 insertions(+), 85 deletions(-)
diff --git a/devtools/cflow.sh b/devtools/cflow.sh
new file mode 100755
index 0000000..5c28e76
--- /dev/null
+++ b/devtools/cflow.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+set -eu
+
+MAIN="${1:-main}"
+
+IGNORE='
+LOG_ERROR
+LOG_INFO
+LOG_WARN
+calloc
+clock_gettime
+fclose
+feof
+ferror
+fgets
+fopen
+fprintf
+fputc
+fputs
+fread
+free
+getpid
+htons
+inet_ntop
+inet_pton
+isspace
+localtime_compat
+localtime_r
+localtime_s
+malloc
+memcmp
+memcpy
+memmove
+memset
+ntohs
+printf
+putc
+putchar
+puts
+realloc
+snprintf
+strchr
+strcmp
+strcpy
+strdup
+strerror
+strftime
+strlen
+strncmp
+strrchr
+strtok_r
+strtol
+strtoul
+time
+trim
+unlink
+'
+
+IGNORE_RE="$(printf '%s\n' "$IGNORE" \
+ | sed '/^[[:space:]]*$/d' \
+ | paste -sd '|' -)"
+
+cflow --brief --main="$MAIN" src/*.c |
+ grep -Ev "^[[:space:]]*(${IGNORE_RE})\\("
diff --git a/src/datagram_route.c b/src/datagram_route.c
index d05f6f7..47b2aea 100644
--- a/src/datagram_route.c
+++ b/src/datagram_route.c
@@ -581,22 +581,14 @@ static void listen_read_cb(evutil_socket_t fd, short events, void *arg)
}
}
-int start_datagram_route(
- struct worker *w,
- const struct route *r,
- struct datagram_route_ctx *ctx
-) {
- if (r->listen.kind != ENDPOINT_INET) {
- LOG_ERROR("unsupported udp listen endpoint",
- "listen", _LOGV_ENDPOINT(&r->listen)
- );
- return -ENOTSUP;
- }
+static int prepare_datagram_route(struct datagram_route_ctx *ctx)
+{
+ const struct route *r = ctx->route;
if (r->upstream.kind != ENDPOINT_INET &&
r->upstream.kind != ENDPOINT_BUILTIN &&
r->upstream.kind != ENDPOINT_UNIX_DGRAM) {
- LOG_ERROR("unsupported udp upstream endpoint",
+ LOG_ERROR("unsupported datagram upstream endpoint",
"upstream", _LOGV_ENDPOINT(&r->upstream)
);
return -ENOTSUP;
@@ -614,7 +606,15 @@ int start_datagram_route(
}
}
+ return 0;
+}
+
+static int bind_udp_datagram_listener(struct datagram_route_ctx *ctx)
+{
+ const struct route *r = ctx->route;
struct sockaddr_in listen_addr;
+ int one = 1;
+
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
@@ -627,16 +627,6 @@ int start_datagram_route(
return -EINVAL;
}
- memset(ctx, 0, sizeof(*ctx));
- if (ctx == NULL) {
- return -ENOMEM;
- }
-
- ctx->base = w->base;
- ctx->worker = w;
- ctx->route = r;
- ctx->listen_fd = -1;
-
ctx->listen_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (ctx->listen_fd < 0) {
int err = EVUTIL_SOCKET_ERROR();
@@ -644,20 +634,16 @@ int start_datagram_route(
LOG_ERROR("udp listen socket failed",
"err", _LOGV(evutil_socket_error_to_string(err))
);
- memset(ctx, 0, sizeof(*ctx));
- return -errno;
+ return -err;
}
evutil_make_socket_closeonexec(ctx->listen_fd);
if (evutil_make_socket_nonblocking(ctx->listen_fd) < 0) {
LOG_ERROR("evutil_make_socket_nonblocking failed");
- evutil_closesocket(ctx->listen_fd);
- free(ctx);
return -EINVAL;
}
- int one = 1;
setsockopt(
ctx->listen_fd,
SOL_SOCKET,
@@ -676,8 +662,6 @@ int start_datagram_route(
LOG_ERROR("udp bind failed",
"err", _LOGV(evutil_socket_error_to_string(err))
);
- evutil_closesocket(ctx->listen_fd);
- free(ctx);
return -EADDRINUSE;
}
@@ -694,8 +678,6 @@ int start_datagram_route(
LOG_ERROR("udp getsockname failed",
"err", _LOGV(evutil_socket_error_to_string(err))
);
- evutil_closesocket(ctx->listen_fd);
- free(ctx);
return -EINVAL;
}
@@ -708,24 +690,63 @@ int start_datagram_route(
);
if (ctx->listen_ev == NULL) {
LOG_ERROR("event_new failed for udp listener");
- evutil_closesocket(ctx->listen_fd);
- free(ctx);
return -ENOMEM;
}
if (event_add(ctx->listen_ev, NULL) < 0) {
LOG_ERROR("event_add failed for udp listener");
- event_free(ctx->listen_ev);
- evutil_closesocket(ctx->listen_fd);
- free(ctx);
return -EINVAL;
}
+ return 0;
+}
+
+int start_datagram_route(
+ struct worker *w,
+ const struct route *r,
+ struct datagram_route_ctx *ctx
+) {
+ int rc;
char opts[128];
+ if (ctx == NULL) {
+ return -EINVAL;
+ }
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->base = w->base;
+ ctx->worker = w;
+ ctx->route = r;
+ ctx->listen_fd = -1;
+
+ rc = prepare_datagram_route(ctx);
+ if (rc != 0) {
+ memset(ctx, 0, sizeof(*ctx));
+ return rc;
+ }
+
+ switch (r->listen.kind) {
+ case ENDPOINT_INET:
+ rc = bind_udp_datagram_listener(ctx);
+ break;
+
+ default:
+ LOG_ERROR("unsupported datagram listen endpoint",
+ "listen", _LOGV_ENDPOINT(&r->listen)
+ );
+ rc = -ENOTSUP;
+ break;
+ }
+
+ if (rc != 0) {
+ stop_datagram_route(ctx);
+ return rc;
+ }
+
route_options_str(&r->opts, opts, sizeof(opts));
- LOG_INFO("udp route started",
+ LOG_INFO("datagram route started",
"line", _LOGV(r->line_no),
"listen", _LOGV_ENDPOINT(&r->listen),
"upstream", _LOGV_ENDPOINT(&r->upstream),
@@ -741,20 +762,23 @@ void stop_datagram_route(struct datagram_route_ctx *ctx)
return;
}
+ while (ctx->clients != NULL) {
+ struct udp_client *c = ctx->clients;
+ ctx->clients = c->next;
+ c->next = NULL;
+ free_udp_client(c);
+ }
+
if (ctx->listen_ev != NULL) {
event_free(ctx->listen_ev);
+ ctx->listen_ev = NULL;
}
if (ctx->listen_fd >= 0) {
evutil_closesocket(ctx->listen_fd);
- }
-
- struct udp_client *c = ctx->clients;
- while (c != NULL) {
- struct udp_client *next = c->next;
- free_udp_client(c);
- c = next;
+ ctx->listen_fd = -1;
}
memset(ctx, 0, sizeof(*ctx));
+ ctx->listen_fd = -1;
}
diff --git a/src/endpoint.c b/src/endpoint.c
index 740936d..53ca6fc 100644
--- a/src/endpoint.c
+++ b/src/endpoint.c
@@ -1,3 +1,4 @@
+#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
@@ -41,4 +42,39 @@ int endpoint_to_string(const struct endpoint *ep, char *buf, size_t buf_len)
}
}
+bool endpoint_is_stream(const struct endpoint *ep)
+{
+ if (ep == NULL) {
+ return false;
+ }
+
+ switch (ep->proto) {
+ case PROTO_TCP:
+ case PROTO_UNIX_STREAM:
+ return true;
+
+ default:
+ return false;
+ }
+}
+bool endpoint_is_datagram(const struct endpoint *ep)
+{
+ if (ep == NULL) {
+ return false;
+ }
+
+ switch (ep->proto) {
+ case PROTO_UDP:
+ case PROTO_UNIX_DGRAM:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool endpoint_is_listenable(const struct endpoint *ep)
+{
+ return endpoint_is_stream(ep) || endpoint_is_datagram(ep);
+}
diff --git a/src/endpoint.h b/src/endpoint.h
index 8ffe14f..867e92a 100644
--- a/src/endpoint.h
+++ b/src/endpoint.h
@@ -8,7 +8,7 @@
enum endpoint_proto {
PROTO_TCP,
PROTO_UDP,
- PROTO_UNIX,
+ PROTO_UNIX_STREAM,
PROTO_UNIX_DGRAM,
PROTO_FILE,
PROTO_BUILTIN,
@@ -37,4 +37,8 @@ struct endpoint {
int endpoint_to_string(const struct endpoint *ep, char *buf, size_t buf_len);
+bool endpoint_is_stream(const struct endpoint *ep);
+bool endpoint_is_datagram(const struct endpoint *ep);
+bool endpoint_is_listenable(const struct endpoint *ep);
+
#endif
diff --git a/src/file_conf.c b/src/file_conf.c
index 776299c..2b3f8e1 100644
--- a/src/file_conf.c
+++ b/src/file_conf.c
@@ -91,7 +91,7 @@ static int parse_proto(const char *s, enum endpoint_proto *out)
}
if (strcmp(s, "unix") == 0) {
- *out = PROTO_UNIX;
+ *out = PROTO_UNIX_STREAM;
return 0;
}
@@ -250,7 +250,7 @@ static int parse_endpoint(enum endpoint_proto proto, const char *s, struct endpo
return 0;
- case PROTO_UNIX:
+ case PROTO_UNIX_STREAM:
path = s;
if (strncmp(path, "unix:", 5) == 0) {
diff --git a/src/route_runtime.c b/src/route_runtime.c
index 089457c..87b0244 100644
--- a/src/route_runtime.c
+++ b/src/route_runtime.c
@@ -4,12 +4,61 @@
#include "route.h"
#include "route_runtime.h"
+int validate_route(const struct route *r)
+{
+ if (r == NULL) {
+ return -EINVAL;
+ }
+
+ if (!endpoint_is_listenable(&r->listen)) {
+ LOG_ERROR("route listen endpoint is not listenable",
+ "line", _LOGV(r->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen)
+ );
+ return -EINVAL;
+ }
+
+ if (endpoint_is_stream(&r->listen)) {
+ if (endpoint_is_stream(&r->upstream) ||
+ r->upstream.proto == PROTO_FILE ||
+ r->upstream.proto == PROTO_BUILTIN) {
+ return 0;
+ }
+
+ LOG_ERROR("unsupported stream route upstream",
+ "line", _LOGV(r->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ return -EINVAL;
+ }
+
+ if (endpoint_is_datagram(&r->listen)) {
+ if (endpoint_is_datagram(&r->upstream) ||
+ r->upstream.proto == PROTO_BUILTIN) {
+ return 0;
+ }
+
+ LOG_ERROR("unsupported datagram route upstream",
+ "line", _LOGV(r->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
int start_route(struct worker *w, const struct route *r, struct route_ctx *ctx)
{
- int rc = 0;
- switch (r->listen.proto) {
+ int rc;
- case PROTO_TCP:
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->route = r;
+ ctx->kind = ROUTE_CTX_NONE;
+
+ if (endpoint_is_stream(&r->listen)) {
ctx->kind = ROUTE_CTX_STREAM;
rc = start_stream_route(w, r, &ctx->u.stream);
@@ -19,35 +68,26 @@ int start_route(struct worker *w, const struct route *r, struct route_ctx *ctx)
}
return rc;
+ }
- case PROTO_UDP:
+ if (endpoint_is_datagram(&r->listen)) {
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));
+ memset(&ctx->u.datagram, 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);
- */
+ LOG_ERROR("route listen endpoint is not listenable",
+ "line", _LOGV(r->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen)
+ );
- default:
- LOG_ERROR("route listen endpoint is not listenable",
- "line", _LOGV(r->line_no),
- "listen", _LOGV_ENDPOINT(&r->listen)
- );
- return -EINVAL;
- }
+ return -EINVAL;
}
void stop_route(struct route_ctx *ctx)
diff --git a/src/route_runtime.h b/src/route_runtime.h
index fb8b359..a192f36 100644
--- a/src/route_runtime.h
+++ b/src/route_runtime.h
@@ -21,6 +21,8 @@ struct route_ctx {
} u;
};
+int validate_route(const struct route *r);
+
int start_route(struct worker *w, const struct route *r, struct route_ctx *ctx);
void stop_route(struct route_ctx *ctx);
diff --git a/src/stream_route.c b/src/stream_route.c
index 7ff1c84..6c782a2 100644
--- a/src/stream_route.c
+++ b/src/stream_route.c
@@ -660,30 +660,21 @@ static void accept_error_cb(struct evconnlistener *listener, void *arg)
event_base_loopexit(ctx->accept_base, NULL);
}
-int start_stream_route(
- struct worker *w,
- const struct route *r,
- struct stream_route_ctx *ctx)
+static int bind_tcp_stream_listener(struct stream_route_ctx *ctx)
{
+ const struct route *r = ctx->route;
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);
if (inet_pton(AF_INET, r->listen.host, &listen_addr.sin_addr) != 1) {
LOG_ERROR("invalid listen address",
- "listen", _LOGV_ENDPOINT(&r->listen)
- );
+ "listen", _LOGV_ENDPOINT(&r->listen));
return -EINVAL;
}
- ctx->accept_base = w->base;
- ctx->worker = w;
- ctx->route = r;
-
ctx->listener = evconnlistener_new_bind(
ctx->accept_base,
accept_cb,
@@ -695,23 +686,53 @@ int start_stream_route(
);
if (ctx->listener == NULL) {
- LOG_ERROR("evconnlistener_new_bind failed");
- memset(ctx, 0, sizeof(*ctx));
+ LOG_ERROR("evconnlistener_new_bind failed",
+ "listen", _LOGV_ENDPOINT(&r->listen));
return -EADDRINUSE;
}
- evconnlistener_set_error_cb(ctx->listener, accept_error_cb);
+ return 0;
+}
+int start_stream_route(struct worker *w, const struct route *r,
+ struct stream_route_ctx *ctx)
+{
+ int rc;
char opts[128];
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->accept_base = w->base;
+ ctx->worker = w;
+ ctx->route = r;
+
+ switch (r->listen.proto) {
+ case PROTO_TCP:
+ rc = bind_tcp_stream_listener(ctx);
+ break;
+
+ default:
+ LOG_ERROR("stream listen protocol not implemented yet",
+ "line", _LOGV(r->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen));
+ rc = -ENOTSUP;
+ break;
+ }
+
+ if (rc != 0) {
+ memset(ctx, 0, sizeof(*ctx));
+ return rc;
+ }
+
+ evconnlistener_set_error_cb(ctx->listener, accept_error_cb);
+
route_options_str(&r->opts, opts, sizeof(opts));
LOG_INFO("route started",
"line", _LOGV(r->line_no),
"listen", _LOGV_ENDPOINT(&r->listen),
"upstream", _LOGV_ENDPOINT(&r->upstream),
- "options", _LOGV(opts[0] ? opts : "")
- );
+ "options", _LOGV(opts[0] ? opts : ""));
return 0;
}
diff --git a/src/tinyproxy.c b/src/tinyproxy.c
index eb146e9..2dc6e3b 100644
--- a/src/tinyproxy.c
+++ b/src/tinyproxy.c
@@ -221,6 +221,16 @@ int main(int argc, char **argv)
for (size_t i = 0; i < route_count; i++) {
const struct route *r = &routes[i];
+ rc = validate_route(r);
+ if (rc != 0) {
+ LOG_ERROR("invalid route",
+ "line", _LOGV(r->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ goto out;
+ }
+
rc = start_route(&w, r, &route_ctxs[route_ctx_count]);
if (rc == 0) {
route_ctx_count++;