commit 2555eb3a3beae0e468e7510b0bf8271637b0174a
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-06-05T17:11:48Z |
| subject | Fixed builtin not properly closing connection |
commit 2555eb3a3beae0e468e7510b0bf8271637b0174a
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-06-05T17:11:48Z
Fixed builtin not properly closing connection
---
examples/minecraft-bedrock-lan.conf | 3 +-
src/stream_builtin.c | 9 ++-
src/stream_conn.c | 119 ++++++++++++++++++++++++++----------
src/stream_conn.h | 1 +
src/stream_pipe.c | 21 -------
5 files changed, 95 insertions(+), 58 deletions(-)
diff --git a/examples/minecraft-bedrock-lan.conf b/examples/minecraft-bedrock-lan.conf
index dc474ca..1f162c9 100644
--- a/examples/minecraft-bedrock-lan.conf
+++ b/examples/minecraft-bedrock-lan.conf
@@ -1,4 +1,3 @@
-#
# Expose a remote Minecraft Bedrock server as a local LAN server.
#
# Clients on the local network can discover the remote server from the
@@ -11,7 +10,7 @@
# Replace 123.123.123.123:19132 with your server address.
#
udp :19132 udp 123.123.123.123:19132 broadcast_reply=listen
-#
+
# Advertise a Minecraft Bedrock server outside the local broadcast subnet.
#
# tinyproxy receives LAN discovery broadcasts on :19132, forwards them to
diff --git a/src/stream_builtin.c b/src/stream_builtin.c
index 3713783..60a1617 100644
--- a/src/stream_builtin.c
+++ b/src/stream_builtin.c
@@ -2,6 +2,7 @@
#include <string.h>
+#include "klog.h"
#include "stream_builtin.h"
#include "stream_conn.h"
@@ -17,10 +18,14 @@ static void builtin_client_read_cb(struct bufferevent *bev, void *arg)
static void builtin_close_after_write_cb(struct bufferevent *bev, void *arg)
{
conn_t *conn = arg;
+ struct evbuffer *output = bufferevent_get_output(bev);
- if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
- free_conn(conn);
+ if (evbuffer_get_length(output) != 0) {
+ return;
}
+
+ finish_client_write(conn);
+ conn->close_after_client_eof = true;
}
static const char *stream_client_addr_string(conn_t *conn, char *buf, size_t buf_len)
diff --git a/src/stream_conn.c b/src/stream_conn.c
index e9bb34b..86466ae 100644
--- a/src/stream_conn.c
+++ b/src/stream_conn.c
@@ -98,6 +98,27 @@ static void drain_client_then_close(conn_t *conn)
}
}
+void finish_client_write(conn_t *conn)
+{
+ evutil_socket_t fd = bufferevent_getfd(conn->client);
+
+ if (fd >= 0) {
+#ifndef _WIN32
+ shutdown(fd, SHUT_WR);
+#else
+ shutdown(fd, SD_SEND);
+#endif
+ }
+
+ bufferevent_disable(conn->client, EV_WRITE);
+
+ /*
+ * Keep EV_READ enabled so we can observe client EOF instead of
+ * closing with unread data and causing RST on some platforms.
+ */
+ bufferevent_enable(conn->client, EV_READ);
+}
+
void stream_client_event_cb(struct bufferevent *bev, short events, void *arg)
{
(void)bev;
@@ -106,40 +127,68 @@ void stream_client_event_cb(struct bufferevent *bev, short events, void *arg)
const struct route *r = conn->route;
if (events & BEV_EVENT_TIMEOUT) {
- LOG_WARN("client connection timed out",
- "listen", _LOGV_ENDPOINT(&r->listen),
- "upstream", _LOGV_ENDPOINT(&r->upstream)
- );
- free_conn(conn);
- return;
+ LOG_DEBUG("client connection timed out",
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ goto out_free;
}
if (events & BEV_EVENT_ERROR) {
int err = EVUTIL_SOCKET_ERROR();
if (conn->close_after_client_eof && err == ECONNRESET) {
- LOG_INFO("client reset after response drain",
+ LOG_DEBUG("client reset after response drain",
"listen", _LOGV_ENDPOINT(&r->listen),
"upstream", _LOGV_ENDPOINT(&r->upstream)
);
- free_conn(conn);
- return;
+ goto out_free;
+ }
+
+ if (err == ECONNRESET) {
+ LOG_DEBUG("client reset connection",
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ goto out_free;
}
- LOG_ERROR("client connection error",
+#ifdef EPIPE
+ if (err == EPIPE) {
+ LOG_DEBUG("client pipe closed",
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ goto out_free;
+ }
+#endif
+
+#ifdef ETIMEDOUT
+ if (err == ETIMEDOUT) {
+ LOG_DEBUG("client connection timed out",
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ goto out_free;
+ }
+#endif
+
+ LOG_WARN("client connection error",
"err", _LOGV(evutil_socket_error_to_string(err)),
"listen", _LOGV_ENDPOINT(&r->listen),
"upstream", _LOGV_ENDPOINT(&r->upstream)
);
-
- free_conn(conn);
- return;
+ goto out_free;
}
if (events & BEV_EVENT_EOF) {
- free_conn(conn);
- return;
+ goto out_free;
}
+
+ return;
+
+out_free:
+ free_conn(conn);
}
void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg)
@@ -147,7 +196,6 @@ void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg)
(void)bev;
conn_t *conn = arg;
-
const struct route *r = conn->route;
if (events & BEV_EVENT_CONNECTED) {
@@ -160,18 +208,16 @@ void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg)
if (events & BEV_EVENT_TIMEOUT) {
LOG_WARN("upstream connection timed out",
- "listen", _LOGV_ENDPOINT(&r->listen),
- "upstream", _LOGV_ENDPOINT(&r->upstream)
- );
- free_conn(conn);
- return;
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ goto out_free;
}
if (events & BEV_EVENT_ERROR) {
int err = EVUTIL_SOCKET_ERROR();
if (client_has_pending_output(conn)) {
-
LOG_WARN("upstream error after response queued; draining client",
"err", _LOGV(evutil_socket_error_to_string(err)),
"client_output", _LOGV(bev_output_len(conn->client)),
@@ -179,30 +225,37 @@ void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg)
"listen", _LOGV_ENDPOINT(&r->listen),
"upstream", _LOGV_ENDPOINT(&r->upstream)
);
-
- drain_client_then_close(conn);
- return;
+ goto out_drain_client;
}
- LOG_ERROR("upstream connection error",
+ LOG_WARN("upstream connection error",
"err", _LOGV(evutil_socket_error_to_string(err)),
"listen", _LOGV_ENDPOINT(&r->listen),
"upstream", _LOGV_ENDPOINT(&r->upstream)
);
-
- free_conn(conn);
- return;
+ goto out_free;
}
if (events & BEV_EVENT_EOF) {
if (client_has_pending_output(conn)) {
- drain_client_then_close(conn);
- return;
+ goto out_drain_client;
}
- free_conn(conn);
- return;
+ goto out_free;
}
+
+ LOG_DEBUG("unhandled upstream event",
+ "events", _LOGV(events),
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+
+out_free:
+ free_conn(conn);
+ return;
+
+out_drain_client:
+ drain_client_then_close(conn);
}
static int connect_upstream(struct bufferevent *bev, const struct endpoint *ep)
diff --git a/src/stream_conn.h b/src/stream_conn.h
index 25b1673..320e2ef 100644
--- a/src/stream_conn.h
+++ b/src/stream_conn.h
@@ -12,6 +12,7 @@ int dispatch_client_fd(struct worker *w,
socklen_t addr_len);
void free_conn(conn_t *conn);
+void finish_client_write(conn_t *conn);
void set_client_idle_timeout(conn_t *conn, const struct route *r);
void stream_client_event_cb(struct bufferevent *bev, short events, void *arg);
diff --git a/src/stream_pipe.c b/src/stream_pipe.c
index fc90233..daa2cdc 100644
--- a/src/stream_pipe.c
+++ b/src/stream_pipe.c
@@ -5,27 +5,6 @@
#include "stream_conn.h"
#include "stream_pipe.h"
-static void finish_client_write(conn_t *conn)
-{
- evutil_socket_t fd = bufferevent_getfd(conn->client);
-
- if (fd >= 0) {
-#ifndef _WIN32
- shutdown(fd, SHUT_WR);
-#else
- shutdown(fd, SD_SEND);
-#endif
- }
-
- bufferevent_disable(conn->client, EV_WRITE);
-
- /*
- * Keep EV_READ enabled so we can observe client EOF instead of
- * closing with unread data and causing RST on some platforms.
- */
- bufferevent_enable(conn->client, EV_READ);
-}
-
void pipe_client_read_cb(struct bufferevent *client, void *arg)
{
conn_t *conn = arg;