Preserve proxy TCP streams across connect, backpressure and FIN
This commit is contained in:
1 parent
09bc8c35c5
commit
e1946b208d
13 files changed
+831
-458
No files matched your search
+13
-1
@@ -282,6 +282,8 @@ static int flush_write_buf(struct tcp_conn* tc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Последний send может опустошить очередь при уже выключенном POLLOUT.
|
||||
// Уведомляем потребителя здесь, не рассчитывая на ещё один write event.
|
||||
static void notify_flushed(struct tcp_conn* tc) {
|
||||
if (tc->destroyed || tc->write_buf || tc->write_queue->head || !tc->on_flushed) return;
|
||||
void (*cb)(struct tcp_conn*, void*) = tc->on_flushed;
|
||||
@@ -316,7 +318,7 @@ static void write_queue_fetch_cb(struct ll_queue* q, void* arg) {
|
||||
if (!e->dgram) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_io: CLOSE fd=%d", (int)tc->sock);
|
||||
queue_entry_free(e); queue_resume_callback(q);
|
||||
uasync_remove_socket_t(tc->ua, tc->sock);
|
||||
if (tc->socket_id) uasync_remove_socket_t(tc->ua, tc->sock);
|
||||
socket_close_wrapper(tc->sock);
|
||||
tc->socket_id = NULL; tc->sock = SOCKET_INVALID; tc->closed = 1;
|
||||
if (tc->on_closed) tc->on_closed(tc, tc->arg);
|
||||
@@ -394,6 +396,8 @@ static void write_cb(socket_t sock, void* arg) {
|
||||
tc->connected = 1;
|
||||
if (tc->connect_timer) { uasync_cancel_timeout(tc->ua, tc->connect_timer); tc->connect_timer = NULL; }
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_io: connect ok fd=%d", (int)tc->sock);
|
||||
if (tc->on_connected) tc->on_connected(tc, tc->arg);
|
||||
if (tc->destroyed) return;
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_io: connect fail fd=%d err=%d", (int)tc->sock, err);
|
||||
tcp_conn_handle_error(tc, err ? err : -1);
|
||||
@@ -426,6 +430,14 @@ static void error_cb(socket_t sock, void* arg) {
|
||||
// Peer полностью закрыл свой сокет (EPOLLHUP) после того, как FIN уже прочитан (fin_remote=1).
|
||||
// EPOLLHUP — level-triggered и «always reported»: если оставить сокет в epoll, каждая итерация
|
||||
// epoll_wait мгновенно вернёт HUP и error_cb зациклится на 100% CPU. Закрываем сокет.
|
||||
if (tc->fin_remote && tc->fin_local) {
|
||||
// Обе TCP-половины закрыты, но владелец ещё может передавать ранее прочитанные данные.
|
||||
// Убираем level-triggered HUP из poll; CLOSE остаётся решением владельца.
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_io: both FINs fd=%d — detach poll, await owner drain", (int)tc->sock);
|
||||
if (tc->socket_id) uasync_remove_socket_t(tc->ua, tc->sock);
|
||||
tc->socket_id = NULL; tc->write_monitor = 0;
|
||||
return;
|
||||
}
|
||||
if (tc->fin_remote) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_io: async HUP (graceful) fd=%d — peer fully closed after FIN, closing",
|
||||
(int)tc->sock);
|
||||
|
||||
@@ -70,6 +70,7 @@ struct tcp_conn {
|
||||
struct queue_waiter_handle read_waiter;
|
||||
|
||||
// Коллбэки
|
||||
void (*on_connected)(struct tcp_conn* tc, void* arg); // исходящий connect завершён
|
||||
void (*on_fin)(struct tcp_conn* tc, void* arg); // FIN получен от удалённой стороны (fin_remote=1)
|
||||
void (*on_fin_sent)(struct tcp_conn* tc, void* arg); // FIN отправлен удалённой стороне (fin_local=1)
|
||||
void (*on_error)(struct tcp_conn* tc, int err, void* arg); // фатальная ошибка (вызывается после close сокета; обязан вызвать tcp_conn_destroy)
|
||||
|
||||
Reference in new issue
Block a user