commit 178260079860545d4d09e44d85eaa71af33b0c22
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-05-25T21:57:50Z |
| subject | Added klog.c |
commit 178260079860545d4d09e44d85eaa71af33b0c22
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-05-25T21:57:50Z
Added klog.c
---
Dockerfile | 21 +++++++++++++++++++++
Makefile | 53 +++++++++++++++++++++++++++++++++++++----------------
README.md | 4 ++--
file_conf.c | 3 ++-
klog.c | 33 +++++++++++++++++++++++++++++++++
klog.h | 10 ++++++++++
tcp_route.c | 26 +++++++++++++-------------
tinyproxy.c | 29 ++++++++++++++---------------
8 files changed, 132 insertions(+), 47 deletions(-)
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..62e9594
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,21 @@
+FROM alpine:3.23 AS build
+
+RUN apk add --no-cache build-base libevent-dev
+
+RUN mkdir /src
+WORKDIR /src
+
+COPY [ "*.c", "*.h", "*.conf", "Makefile", "/src" ]
+
+RUN make all
+
+COPY tests ./tests
+RUN make test
+
+FROM alpine:3.23
+
+RUN apk add --no-cache libevent
+COPY --from=build /src/bin/tinyproxy /usr/bin/tinyproxy
+COPY --from=build /src/tinyproxy.conf /etc/tinyproxy.conf
+
+CMD [ "tinyproxy", "-c", "/etc/tinyproxy.conf" ]
diff --git a/Makefile b/Makefile
index 61ca864..50a34f8 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,8 @@ STRIP ?= strip
PROJECT_ROOT := $(CURDIR)
BIN_DIR := $(CURDIR)/bin
-SRC := proxy_proto_v2.c \
+SRC := klog.c \
+ proxy_proto_v2.c \
route.c \
tcp_route.c \
file_conf.c \
@@ -18,33 +19,39 @@ BIN := $(BIN_DIR)/tinyproxy
BUILD_DIR := $(PROJECT_ROOT)/build
-LIBEVENT_SRC := $(PROJECT_ROOT)/libevent2
+# Optional. Set this only when you want to build vendored libevent.
+# Example:
+# make LIBEVENT_SRC=$(PROJECT_ROOT)/libevent2
+LIBEVENT_SRC ?=
+
LIBEVENT_PREFIX := $(BUILD_DIR)/libevent-install
LIBEVENT_CORE_A := $(LIBEVENT_PREFIX)/lib/libevent_core.a
CFLAGS ?= -Os -Wall -Wextra -ffunction-sections -fdata-sections
-CPPFLAGS += -I$(LIBEVENT_PREFIX)/include
-LDFLAGS += -L$(LIBEVENT_PREFIX)/lib
-ifeq ($(shell uname),Darwin)
-LDFLAGS += -Wl,-dead_strip
-TEST_FLAGS := CONCURRENCY=1000 TOTAL=1000 FD_LIMIT=2560
+ifeq ($(strip $(LIBEVENT_SRC)),)
+# System libevent, e.g. Alpine libevent-dev
+LIBEVENT_CPPFLAGS := $(shell pkg-config --cflags libevent_core)
+LIBEVENT_LDFLAGS := $(shell pkg-config --libs-only-L libevent_core)
+LIBEVENT_LDLIBS := $(shell pkg-config --libs-only-l --libs-only-other libevent_core)
+LIBEVENT_DEPS :=
else
-LDFLAGS += -Wl,--gc-sections
-TEST_FLAGS :=
+# Vendored libevent
+LIBEVENT_CPPFLAGS := -I$(LIBEVENT_PREFIX)/include
+LIBEVENT_LDFLAGS := -L$(LIBEVENT_PREFIX)/lib
+LIBEVENT_LDLIBS := -levent_core
+LIBEVENT_DEPS := $(LIBEVENT_CORE_A)
endif
-LDLIBS += -levent_core
+CPPFLAGS += $(LIBEVENT_CPPFLAGS)
+LDFLAGS += $(LIBEVENT_LDFLAGS)
+LDLIBS += $(LIBEVENT_LDLIBS)
-all: $(BIN)
-
-$(BIN): $(SRC) $(LIBEVENT_CORE_A)
+$(BIN): $(SRC) $(LIBEVENT_DEPS)
mkdir -p $(BIN_DIR)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(SRC) $(LDFLAGS) $(LDLIBS)
-strip: $(BIN)
- $(STRIP) $(BIN)
-
+ifneq ($(strip $(LIBEVENT_SRC)),)
$(LIBEVENT_CORE_A):
cd $(LIBEVENT_SRC) && \
AR=$(AR) RANLIB=$(RANLIB) ./configure \
@@ -60,6 +67,20 @@ $(LIBEVENT_CORE_A):
--disable-shared
AR=$(AR) RANLIB=$(RANLIB) $(MAKE) -C $(LIBEVENT_SRC)
AR=$(AR) RANLIB=$(RANLIB) $(MAKE) -C $(LIBEVENT_SRC) install
+endif
+
+ifeq ($(shell uname),Darwin)
+LDFLAGS += -Wl,-dead_strip
+TEST_FLAGS := CONCURRENCY=1000 TOTAL=1000 FD_LIMIT=2560
+else
+LDFLAGS += -Wl,--gc-sections
+TEST_FLAGS :=
+endif
+
+all: $(BIN)
+
+strip: $(BIN)
+ $(STRIP) $(BIN)
test: $(BIN)
$(TEST_FLAGS) python3 tests/test_proxy.py $(BIN)
diff --git a/README.md b/README.md
index bbdfa93..304a0ea 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@ tinyproxy
This is a work in progress project.
-## Rules of thumb
-1. Work as a simple inet utils
+## Rule of thumb
+1. Should work like an inet utils
1. Be simple and effective
1. Runs in foreground
1. Uses libevent2 (for readability and future maintainance)
diff --git a/file_conf.c b/file_conf.c
index 0d12b82..5d86348 100644
--- a/file_conf.c
+++ b/file_conf.c
@@ -5,6 +5,7 @@
#include <ctype.h>
#include <errno.h>
+#include "klog.h"
#include "file_conf.h"
#define MAX_LINE_LEN 512
@@ -194,7 +195,7 @@ int load_routes_from_file(const char *path, struct route **routes_out, size_t *c
struct route *r = &routes[count];
if (parse_route_line(line, r) != 0) {
- fprintf(stderr, "%s:%u: invalid route config\n", path, line_no);
+ LOG_ERROR("%s:%u: invalid route config", path, line_no);
fclose(fp);
free(routes);
return -EINVAL;
diff --git a/klog.c b/klog.c
new file mode 100644
index 0000000..451316a
--- /dev/null
+++ b/klog.c
@@ -0,0 +1,33 @@
+#include "klog.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+void log_at(char sev, const char *file, int line, const char *fmt, ...)
+{
+ struct timespec ts;
+ struct tm tm;
+ char tbuf[32];
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ localtime_r(&ts.tv_sec, &tm);
+
+ strftime(tbuf, sizeof(tbuf), "%m%d %H:%M:%S", &tm);
+
+ fprintf(stderr, "%c%s.%06ld %7ld %s:%d] ",
+ sev,
+ tbuf,
+ ts.tv_nsec / 1000,
+ (long)getpid(),
+ file,
+ line);
+
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fputc('\n', stderr);
+}
diff --git a/klog.h b/klog.h
new file mode 100644
index 0000000..b4c24b5
--- /dev/null
+++ b/klog.h
@@ -0,0 +1,10 @@
+#ifndef KLOG_H
+#define KLOG_H
+
+void log_at(char sev, const char *file, int line, const char *fmt, ...);
+
+#define LOG_INFO(...) log_at('I', __FILE__, __LINE__, __VA_ARGS__)
+#define LOG_WARN(...) log_at('W', __FILE__, __LINE__, __VA_ARGS__)
+#define LOG_ERROR(...) log_at('E', __FILE__, __LINE__, __VA_ARGS__)
+
+#endif
diff --git a/tcp_route.c b/tcp_route.c
index 84cbdeb..fcd5240 100644
--- a/tcp_route.c
+++ b/tcp_route.c
@@ -10,6 +10,7 @@
#include <stdlib.h>
#include <string.h>
+#include "klog.h"
#include "file_conf.h"
#include "proxy_proto_v2.h"
#include "route.h"
@@ -64,8 +65,7 @@ static void event_cb(struct bufferevent *bev, short events, void *arg) {
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
if (events & BEV_EVENT_ERROR) {
int err = EVUTIL_SOCKET_ERROR();
- fprintf(stderr, "connection error: %s\n",
- evutil_socket_error_to_string(err));
+ LOG_ERROR("connection error: %s", evutil_socket_error_to_string(err));
}
free_conn(conn);
@@ -119,7 +119,7 @@ static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac)
upstream_addr.sin_port = htons(r->upstream_port);
if (inet_pton(AF_INET, r->upstream_host, &upstream_addr.sin_addr) != 1) {
- fprintf(stderr, "invalid upstream address\n");
+ LOG_ERROR("invalid upstream address");
free_conn(conn);
return;
}
@@ -137,7 +137,7 @@ static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac)
(struct sockaddr *)&upstream_addr,
sizeof(upstream_addr)
) < 0) {
- fprintf(stderr, "upstream connect failed\n");
+ LOG_ERROR("upstream connect failed");
free_conn(conn);
return;
}
@@ -155,14 +155,14 @@ static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac)
}
if (0 < ac->peer_addr_len) {
- fprintf(stderr, "invalid client address\n");
+ LOG_ERROR("invalid client address");
free_conn(conn);
return;
}
if (ac->peer_addr.ss_family != AF_INET ||
local_addr.sin_family != AF_INET) {
- fprintf(stderr, "PROXY v2 currently only supports IPv4 TCP\n");
+ LOG_ERROR("PROXY v2 currently only supports IPv4 TCP");
free_conn(conn);
return;
}
@@ -175,7 +175,7 @@ static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac)
local_len,
SOCK_STREAM
) < 0) {
- fprintf(stderr, "failed to write PROXY v2 header\n");
+ LOG_ERROR("failed to write PROXY v2 header");
free_conn(conn);
return;
}
@@ -217,8 +217,7 @@ static void accept_error_cb(struct evconnlistener *listener, void *arg) {
struct event_base *base = arg;
int err = EVUTIL_SOCKET_ERROR();
- fprintf(stderr, "accept error: %s\n",
- evutil_socket_error_to_string(err));
+ LOG_ERROR("accept error: %s", evutil_socket_error_to_string(err));
evconnlistener_free(listener);
event_base_loopexit(base, NULL);
@@ -235,7 +234,7 @@ int start_tcp_route(
listen_addr.sin_port = htons(r->listen_port);
if (inet_pton(AF_INET, r->listen_host, &listen_addr.sin_addr) != 1) {
- fprintf(stderr, "invalid listen address: %s\n", r->listen_host);
+ LOG_ERROR("invalid listen address: %s", r->listen_host);
return -EINVAL;
}
@@ -259,7 +258,7 @@ int start_tcp_route(
);
if (ctx->listener == NULL) {
- fprintf(stderr, "evconnlistener_new_bind failed\n");
+ LOG_ERROR("evconnlistener_new_bind failed");
free(ctx);
return -EADDRINUSE;
}
@@ -270,11 +269,12 @@ int start_tcp_route(
route_options_str(r, opts, sizeof(opts));
- fprintf(stderr, "line %u, listening on %s:%u, forwarding to %s:%u%s%s\n",
+ LOG_INFO("msg=\"route started\" line=%u listen_host=%s listen_port=%u upstream_host=%s upstream_port=%u%s%s",
r->line_no,
r->listen_host, r->listen_port,
r->upstream_host, r->upstream_port,
- opts[0] ? ", options: " : "", opts
+ opts[0] ? " options=" : "",
+ opts[0] ? opts : ""
);
*out = ctx;
diff --git a/tinyproxy.c b/tinyproxy.c
index 6eb89ca..171e483 100644
--- a/tinyproxy.c
+++ b/tinyproxy.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <unistd.h>
+#include "klog.h"
#include "file_conf.h"
#include "tcp_route.h"
@@ -30,7 +31,7 @@ int main(int argc, char **argv)
switch (opt) {
case 'c':
if (optarg == NULL || optarg[0] == '\0') {
- fprintf(stderr, "missing config path after -c\n");
+ LOG_ERROR("missing config path after -c");
return 2;
}
conf_path = optarg;
@@ -51,7 +52,7 @@ int main(int argc, char **argv)
}
if (optind != argc) {
- fprintf(stderr, "unexpected argument: %s\n", argv[optind]);
+ LOG_ERROR("unexpected argument: %s", argv[optind]);
usage(stderr, argv[0]);
return 2;
}
@@ -61,13 +62,12 @@ int main(int argc, char **argv)
int rc = load_routes_from_file(conf_path, &routes, &route_count);
if (rc != 0) {
- fprintf(stderr, "failed to load config %s: %s\n",
- conf_path, strerror(-rc));
+ LOG_ERROR("failed to load config %s: %s", conf_path, strerror(-rc));
return 1;
}
if (route_count == 0) {
- fprintf(stderr, "config %s has no routes\n", conf_path);
+ LOG_ERROR("config %s has no routes", conf_path);
free_routes(routes);
return 1;
}
@@ -82,7 +82,7 @@ int main(int argc, char **argv)
struct event_base *base = event_base_new();
if (base == NULL) {
- fprintf(stderr, "event_base_new failed\n");
+ LOG_ERROR("event_base_new failed");
return 1;
}
@@ -104,22 +104,21 @@ int main(int argc, char **argv)
case PROTO_UDP:
// rc = start_udp_route(r);
- fprintf(stderr, "skipping udp route: Not implemented yet\n");
+ LOG_WARN("skipping udp route: Not implemented yet");
break;
default:
- fprintf(stderr, "route %zu has unknown protocol\n", i);
+ LOG_ERROR("route %zu has unknown protocol", i);
rc = -EINVAL;
break;
}
if (rc != 0) {
- fprintf(stderr,
- "failed to start route %zu: %s:%u -> %s:%u: %s\n",
- i,
- r->listen_host, r->listen_port,
- r->upstream_host, r->upstream_port,
- strerror(-rc));
+ LOG_ERROR("msg=\"failed to start route\" line=%u listen_host=%s listen_port=%u upstream_host=%s upstream_port=%u",
+ r->line_no,
+ r->listen_host, r->listen_port,
+ r->upstream_host, r->upstream_port
+ );
for (size_t j = 0; j < tcp_ctx_count; j++) {
free_tcp_route(tcp_ctxs[j]);
@@ -143,7 +142,7 @@ int main(int argc, char **argv)
free_routes(routes);
if (rc != 0) {
- fprintf(stderr, "event loop failed: %s\n", strerror(-rc));
+ LOG_ERROR("event loop failed: %s", strerror(-rc));
return 1;
}