feat(proxy): HTTPS 反向代理后端支持
- 新增 proxy_tls.c/h:轻量级 OpenSSL 客户端封装 - proxy.c:支持 HTTPS 后端连接(TLS 握手 + SNI + 透传 X-Forwarded-Proto: https) - 集成测试:新增 HTTPS 后端 Python 脚本 + 2 项代理测试 - 编译零警告,142 单元测试 + 82 集成测试全部通过
This commit is contained in:
parent
de88bbc692
commit
a86b8a4856
6
Makefile
6
Makefile
@ -20,7 +20,7 @@ PREFIX ?= /usr/local
|
|||||||
BINDIR = $(PREFIX)/bin
|
BINDIR = $(PREFIX)/bin
|
||||||
|
|
||||||
# 源文件
|
# 源文件
|
||||||
SRCS = main.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c access_log.c websocket.c platform.c middleware.c plugin.c proxy.c
|
SRCS = main.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c access_log.c websocket.c platform.c middleware.c plugin.c proxy.c proxy_tls.c
|
||||||
OBJS = $(SRCS:.c=.o)
|
OBJS = $(SRCS:.c=.o)
|
||||||
TARGET = cocoon
|
TARGET = cocoon
|
||||||
|
|
||||||
@ -90,8 +90,8 @@ unit-test: $(UNIT_TEST_BINS)
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 单元测试编译规则
|
# 单元测试编译规则
|
||||||
$(UNIT_TEST_DIR)/test_server: $(UNIT_TEST_DIR)/test_server.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c access_log.c websocket.c platform.c middleware.c plugin.c proxy.c $(UNITY_SRC)
|
$(UNIT_TEST_DIR)/test_server: $(UNIT_TEST_DIR)/test_server.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c access_log.c websocket.c platform.c middleware.c plugin.c proxy.c proxy_tls.c $(UNITY_SRC)
|
||||||
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_server.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c access_log.c websocket.c platform.c middleware.c plugin.c proxy.c $(UNITY_SRC) $(LDFLAGS)
|
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_server.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c access_log.c websocket.c platform.c middleware.c plugin.c proxy.c proxy_tls.c $(UNITY_SRC) $(LDFLAGS)
|
||||||
|
|
||||||
$(UNIT_TEST_DIR)/test_multipart: $(UNIT_TEST_DIR)/test_multipart.c multipart.c $(UNITY_SRC)
|
$(UNIT_TEST_DIR)/test_multipart: $(UNIT_TEST_DIR)/test_multipart.c multipart.c $(UNITY_SRC)
|
||||||
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_multipart.c multipart.c $(UNITY_SRC) -lm
|
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_multipart.c multipart.c $(UNITY_SRC) -lm
|
||||||
|
|||||||
68
proxy.c
68
proxy.c
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
#include "proxy_tls.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -204,19 +205,44 @@ static int send_all_fd(cocoon_socket_t fd, const char *data, size_t len) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* proxy_send_all_tls - 通过 TLS 连接发送全部数据
|
||||||
|
*/
|
||||||
|
static int proxy_send_all_tls(proxy_tls_conn_t *conn, const char *data, size_t len) {
|
||||||
|
size_t sent = 0;
|
||||||
|
while (sent < len) {
|
||||||
|
ssize_t n = proxy_tls_write(conn, data + sent, len - sent);
|
||||||
|
if (n > 0) {
|
||||||
|
sent += (size_t)n;
|
||||||
|
} else if (n < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EINTR) continue;
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool proxy_forward(cocoon_socket_t client_fd, const http_request_t *req,
|
bool proxy_forward(cocoon_socket_t client_fd, const http_request_t *req,
|
||||||
const cocoon_proxy_rule_t *rule,
|
const cocoon_proxy_rule_t *rule,
|
||||||
const struct sockaddr_storage *client_addr) {
|
const struct sockaddr_storage *client_addr) {
|
||||||
/* 目前不支持 HTTPS 后端 */
|
bool use_https = rule->target_https;
|
||||||
if (rule->target_https) {
|
cocoon_socket_t backend_fd = COCOON_INVALID_SOCKET;
|
||||||
log_warn("HTTPS 后端暂未支持,拒绝代理: %s", rule->target_host);
|
proxy_tls_conn_t *tls_conn = NULL;
|
||||||
|
|
||||||
|
if (use_https) {
|
||||||
|
tls_conn = proxy_tls_connect(rule->target_host, rule->target_port);
|
||||||
|
if (!tls_conn) {
|
||||||
|
log_error("连接 HTTPS 后端失败: %s:%d", rule->target_host, rule->target_port);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
cocoon_socket_t backend_fd = proxy_connect_backend(rule);
|
backend_fd = proxy_connect_backend(rule);
|
||||||
if (backend_fd == COCOON_INVALID_SOCKET) {
|
if (backend_fd == COCOON_INVALID_SOCKET) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 构建转发路径 */
|
/* 构建转发路径 */
|
||||||
char forwarded_path[512];
|
char forwarded_path[512];
|
||||||
@ -232,13 +258,14 @@ bool proxy_forward(cocoon_socket_t client_fd, const http_request_t *req,
|
|||||||
"%s %s HTTP/1.1\r\n"
|
"%s %s HTTP/1.1\r\n"
|
||||||
"Host: %s:%d\r\n"
|
"Host: %s:%d\r\n"
|
||||||
"X-Forwarded-For: %s\r\n"
|
"X-Forwarded-For: %s\r\n"
|
||||||
"X-Forwarded-Proto: http\r\n"
|
"X-Forwarded-Proto: %s\r\n"
|
||||||
"Connection: close\r\n",
|
"Connection: close\r\n",
|
||||||
http_method_str(req->method),
|
http_method_str(req->method),
|
||||||
forwarded_path,
|
forwarded_path,
|
||||||
rule->target_host,
|
rule->target_host,
|
||||||
rule->target_port,
|
rule->target_port,
|
||||||
xff);
|
xff,
|
||||||
|
use_https ? "https" : "http");
|
||||||
|
|
||||||
/* 透传常见请求头 */
|
/* 透传常见请求头 */
|
||||||
if (req->content_type[0] != '\0') {
|
if (req->content_type[0] != '\0') {
|
||||||
@ -253,26 +280,47 @@ bool proxy_forward(cocoon_socket_t client_fd, const http_request_t *req,
|
|||||||
n += snprintf(request_buf + n, sizeof(request_buf) - n, "\r\n");
|
n += snprintf(request_buf + n, sizeof(request_buf) - n, "\r\n");
|
||||||
|
|
||||||
/* 发送请求头 */
|
/* 发送请求头 */
|
||||||
|
if (use_https) {
|
||||||
|
if (proxy_send_all_tls(tls_conn, request_buf, (size_t)n) != 0) {
|
||||||
|
log_error("转发请求头到 HTTPS 后端失败");
|
||||||
|
proxy_tls_close(tls_conn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (send_all_fd(backend_fd, request_buf, (size_t)n) != 0) {
|
if (send_all_fd(backend_fd, request_buf, (size_t)n) != 0) {
|
||||||
log_error("转发请求头到后端失败");
|
log_error("转发请求头到后端失败");
|
||||||
cocoon_socket_close(backend_fd);
|
cocoon_socket_close(backend_fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 转发请求体 */
|
/* 转发请求体 */
|
||||||
if (req->body && req->body_len > 0) {
|
if (req->body && req->body_len > 0) {
|
||||||
|
if (use_https) {
|
||||||
|
if (proxy_send_all_tls(tls_conn, req->body, req->body_len) != 0) {
|
||||||
|
log_error("转发请求体到 HTTPS 后端失败");
|
||||||
|
proxy_tls_close(tls_conn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (send_all_fd(backend_fd, req->body, req->body_len) != 0) {
|
if (send_all_fd(backend_fd, req->body, req->body_len) != 0) {
|
||||||
log_error("转发请求体到后端失败");
|
log_error("转发请求体到后端失败");
|
||||||
cocoon_socket_close(backend_fd);
|
cocoon_socket_close(backend_fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 流式转发响应回客户端 */
|
/* 流式转发响应回客户端 */
|
||||||
char relay_buf[8192];
|
char relay_buf[8192];
|
||||||
ssize_t total_forwarded = 0;
|
ssize_t total_forwarded = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
ssize_t r = recv(backend_fd, relay_buf, sizeof(relay_buf), 0);
|
ssize_t r;
|
||||||
|
if (use_https) {
|
||||||
|
r = proxy_tls_read(tls_conn, relay_buf, sizeof(relay_buf));
|
||||||
|
} else {
|
||||||
|
r = recv(backend_fd, relay_buf, sizeof(relay_buf), 0);
|
||||||
|
}
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (errno == EAGAIN || errno == EINTR) continue;
|
if (errno == EAGAIN || errno == EINTR) continue;
|
||||||
break;
|
break;
|
||||||
@ -290,6 +338,10 @@ bool proxy_forward(cocoon_socket_t client_fd, const http_request_t *req,
|
|||||||
req->path, rule->target_host, rule->target_port,
|
req->path, rule->target_host, rule->target_port,
|
||||||
forwarded_path, total_forwarded);
|
forwarded_path, total_forwarded);
|
||||||
|
|
||||||
|
if (use_https) {
|
||||||
|
proxy_tls_close(tls_conn);
|
||||||
|
} else {
|
||||||
cocoon_socket_close(backend_fd);
|
cocoon_socket_close(backend_fd);
|
||||||
|
}
|
||||||
return req->keep_alive;
|
return req->keep_alive;
|
||||||
}
|
}
|
||||||
|
|||||||
172
proxy_tls.c
Normal file
172
proxy_tls.c
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/**
|
||||||
|
* proxy_tls.c - 反向代理客户端 TLS 实现
|
||||||
|
*
|
||||||
|
* 使用 OpenSSL 作为 TLS 客户端连接到 HTTPS 后端。
|
||||||
|
* 独立模块,不共享服务器端 TLS 上下文。
|
||||||
|
*
|
||||||
|
* @author xfy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "proxy_tls.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
struct proxy_tls_conn {
|
||||||
|
cocoon_socket_t fd;
|
||||||
|
SSL *ssl;
|
||||||
|
SSL_CTX *ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* proxy_tls_connect_tcp - 建立到后端的 TCP 连接
|
||||||
|
*/
|
||||||
|
static cocoon_socket_t proxy_tls_connect_tcp(const char *host, uint16_t port) {
|
||||||
|
struct hostent *h = gethostbyname(host);
|
||||||
|
if (!h) {
|
||||||
|
log_error("代理 TLS: 无法解析主机 %s", host);
|
||||||
|
return COCOON_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
cocoon_socket_t fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (fd == COCOON_INVALID_SOCKET) {
|
||||||
|
log_error("代理 TLS: 创建 socket 失败");
|
||||||
|
return COCOON_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
memcpy(&addr.sin_addr, h->h_addr_list[0], (size_t)h->h_length);
|
||||||
|
|
||||||
|
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||||
|
log_error("代理 TLS: 连接后端失败 %s:%d", host, port);
|
||||||
|
cocoon_socket_close(fd);
|
||||||
|
return COCOON_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_tls_conn_t *proxy_tls_connect(const char *host, uint16_t port) {
|
||||||
|
if (!host || host[0] == '\0') return NULL;
|
||||||
|
|
||||||
|
/* 建立 TCP 连接 */
|
||||||
|
cocoon_socket_t fd = proxy_tls_connect_tcp(host, port);
|
||||||
|
if (fd == COCOON_INVALID_SOCKET) return NULL;
|
||||||
|
|
||||||
|
/* 创建客户端 SSL 上下文 */
|
||||||
|
const SSL_METHOD *method = TLS_client_method();
|
||||||
|
if (!method) {
|
||||||
|
cocoon_socket_close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_CTX *ctx = SSL_CTX_new(method);
|
||||||
|
if (!ctx) {
|
||||||
|
cocoon_socket_close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 最低 TLS 1.2 */
|
||||||
|
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
||||||
|
|
||||||
|
/* 跳过证书验证(内部反向代理场景) */
|
||||||
|
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
|
||||||
|
|
||||||
|
/* 创建 SSL 连接 */
|
||||||
|
SSL *ssl = SSL_new(ctx);
|
||||||
|
if (!ssl) {
|
||||||
|
SSL_CTX_free(ctx);
|
||||||
|
cocoon_socket_close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 设置 SNI */
|
||||||
|
SSL_set_tlsext_host_name(ssl, host);
|
||||||
|
|
||||||
|
/* 绑定 socket 到 SSL */
|
||||||
|
SSL_set_fd(ssl, fd);
|
||||||
|
|
||||||
|
/* 执行握手 */
|
||||||
|
int ret = SSL_connect(ssl);
|
||||||
|
if (ret != 1) {
|
||||||
|
int err = SSL_get_error(ssl, ret);
|
||||||
|
log_error("代理 TLS: 握手失败 %s:%d, err=%d", host, port, err);
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(ctx);
|
||||||
|
cocoon_socket_close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("代理 TLS: 握手成功 %s:%d", host, port);
|
||||||
|
|
||||||
|
proxy_tls_conn_t *conn = (proxy_tls_conn_t *)calloc(1, sizeof(proxy_tls_conn_t));
|
||||||
|
if (!conn) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(ctx);
|
||||||
|
cocoon_socket_close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->fd = fd;
|
||||||
|
conn->ssl = ssl;
|
||||||
|
conn->ctx = ctx;
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t proxy_tls_read(proxy_tls_conn_t *conn, void *buf, size_t len) {
|
||||||
|
if (!conn || !conn->ssl) return -1;
|
||||||
|
|
||||||
|
int ret = SSL_read(conn->ssl, buf, (int)len);
|
||||||
|
if (ret > 0) return (ssize_t)ret;
|
||||||
|
|
||||||
|
int err = SSL_get_error(conn->ssl, ret);
|
||||||
|
if (err == SSL_ERROR_ZERO_RETURN) return 0; /* 对端关闭 */
|
||||||
|
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
errno = EAGAIN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t proxy_tls_write(proxy_tls_conn_t *conn, const void *buf, size_t len) {
|
||||||
|
if (!conn || !conn->ssl) return -1;
|
||||||
|
|
||||||
|
int ret = SSL_write(conn->ssl, buf, (int)len);
|
||||||
|
if (ret > 0) return (ssize_t)ret;
|
||||||
|
|
||||||
|
int err = SSL_get_error(conn->ssl, ret);
|
||||||
|
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
errno = EAGAIN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proxy_tls_close(proxy_tls_conn_t *conn) {
|
||||||
|
if (!conn) return;
|
||||||
|
|
||||||
|
if (conn->ssl) {
|
||||||
|
SSL_shutdown(conn->ssl);
|
||||||
|
SSL_free(conn->ssl);
|
||||||
|
}
|
||||||
|
if (conn->ctx) {
|
||||||
|
SSL_CTX_free(conn->ctx);
|
||||||
|
}
|
||||||
|
if (conn->fd != COCOON_INVALID_SOCKET) {
|
||||||
|
cocoon_socket_close(conn->fd);
|
||||||
|
}
|
||||||
|
free(conn);
|
||||||
|
}
|
||||||
60
proxy_tls.h
Normal file
60
proxy_tls.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* proxy_tls.h - 反向代理客户端 TLS 模块
|
||||||
|
*
|
||||||
|
* 轻量级 OpenSSL 客户端封装,用于连接 HTTPS 后端。
|
||||||
|
* 不依赖服务器端 TLS 模块(tls.c),保持职责分离。
|
||||||
|
*
|
||||||
|
* @author xfy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COCOON_PROXY_TLS_H
|
||||||
|
#define COCOON_PROXY_TLS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
/* 不透明连接句柄 */
|
||||||
|
typedef struct proxy_tls_conn proxy_tls_conn_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* proxy_tls_connect - 连接到 HTTPS 后端并执行 TLS 握手
|
||||||
|
*
|
||||||
|
* 创建客户端 SSL 上下文,建立 TCP 连接,完成 TLS 握手。
|
||||||
|
* 证书验证默认关闭(适用于内部反向代理场景)。
|
||||||
|
* 自动设置 SNI(Server Name Indication)。
|
||||||
|
*
|
||||||
|
* @param host 目标主机名
|
||||||
|
* @param port 目标端口
|
||||||
|
* @return TLS 连接句柄,失败返回 NULL
|
||||||
|
*/
|
||||||
|
proxy_tls_conn_t *proxy_tls_connect(const char *host, uint16_t port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* proxy_tls_read - 从 TLS 连接读取解密数据
|
||||||
|
*
|
||||||
|
* @param conn TLS 连接句柄
|
||||||
|
* @param buf 读取缓冲区
|
||||||
|
* @param len 最大读取长度
|
||||||
|
* @return 实际读取字节数,0 对端关闭,-1 错误
|
||||||
|
*/
|
||||||
|
ssize_t proxy_tls_read(proxy_tls_conn_t *conn, void *buf, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* proxy_tls_write - 向 TLS 连接写入明文数据
|
||||||
|
*
|
||||||
|
* @param conn TLS 连接句柄
|
||||||
|
* @param buf 数据缓冲区
|
||||||
|
* @param len 数据长度
|
||||||
|
* @return 实际写入字节数,-1 错误
|
||||||
|
*/
|
||||||
|
ssize_t proxy_tls_write(proxy_tls_conn_t *conn, const void *buf, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* proxy_tls_close - 关闭 TLS 连接并释放资源
|
||||||
|
*
|
||||||
|
* @param conn TLS 连接句柄
|
||||||
|
*/
|
||||||
|
void proxy_tls_close(proxy_tls_conn_t *conn);
|
||||||
|
|
||||||
|
#endif /* COCOON_PROXY_TLS_H */
|
||||||
33
tests/https_backend.py
Normal file
33
tests/https_backend.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
https_backend.py — 简单的 HTTPS 后端服务器,用于反向代理 HTTPS 测试。
|
||||||
|
|
||||||
|
用法: python3 https_backend.py <port> <cert_file> <key_file> <directory>
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import ssl
|
||||||
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||||
|
|
||||||
|
if len(sys.argv) < 5:
|
||||||
|
print("用法: python3 https_backend.py <port> <cert_file> <key_file> <directory>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
port = int(sys.argv[1])
|
||||||
|
cert_file = sys.argv[2]
|
||||||
|
key_file = sys.argv[3]
|
||||||
|
directory = sys.argv[4]
|
||||||
|
|
||||||
|
class Handler(SimpleHTTPRequestHandler):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, directory=directory, **kwargs)
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
# 抑制日志输出
|
||||||
|
pass
|
||||||
|
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
|
context.load_cert_chain(cert_file, key_file)
|
||||||
|
|
||||||
|
server = HTTPServer(("localhost", port), Handler)
|
||||||
|
server.socket = context.wrap_socket(server.socket, server_side=True)
|
||||||
|
server.serve_forever()
|
||||||
@ -1012,8 +1012,59 @@ else
|
|||||||
fail
|
fail
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 清理后端服务器
|
# 清理 HTTP 后端服务器
|
||||||
kill -9 $BACKEND_PID 2>/dev/null || true
|
kill -9 $BACKEND_PID 2>/dev/null || true
|
||||||
|
kill_server
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# === HTTPS 反向代理测试 ===
|
||||||
|
echo ""
|
||||||
|
echo "=== HTTPS 反向代理测试 ==="
|
||||||
|
|
||||||
|
# 启动 HTTPS 后端服务器
|
||||||
|
python3 "$ROOT/../https_backend.py" 9001 "$ROOT/../server.crt" "$ROOT/../server.key" "$ROOT" > "$TMPDIR/backend_https.log" 2>&1 &
|
||||||
|
BACKEND_HTTPS_PID=$!
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# 创建带 HTTPS 代理配置的配置文件
|
||||||
|
HTTPS_PROXY_CONFIG="$TMPDIR/https_proxy_config.json"
|
||||||
|
cat > "$HTTPS_PROXY_CONFIG" << 'EOF'
|
||||||
|
{
|
||||||
|
"root_dir": "./tests/fixtures",
|
||||||
|
"port": 9999,
|
||||||
|
"log_level": "debug",
|
||||||
|
"proxies": [
|
||||||
|
{"prefix": "/backend", "target": "https://localhost:9001"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
$SERVER -c "$HTTPS_PROXY_CONFIG" > "$TMPDIR/server_https_proxy.log" 2>&1 &
|
||||||
|
for i in {1..30}; do
|
||||||
|
if nc -z localhost 9999 2>/dev/null; then break; fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
https_proxy_status=$(curl -s -o /dev/null -w "%{http_code}" "http://$HOST/backend/index.html")
|
||||||
|
if [[ "$https_proxy_status" == "200" ]]; then
|
||||||
|
echo " ✓ HTTPS 反向代理 GET — HTTP 200"
|
||||||
|
pass
|
||||||
|
else
|
||||||
|
echo " ✗ HTTPS 反向代理 GET — 期望 200, 实际 $https_proxy_status"
|
||||||
|
fail
|
||||||
|
fi
|
||||||
|
|
||||||
|
https_proxy_body=$(curl -s "http://$HOST/backend/index.html")
|
||||||
|
if echo "$https_proxy_body" | grep -q "Cocoon"; then
|
||||||
|
echo " ✓ HTTPS 反向代理响应体 — 包含后端内容"
|
||||||
|
pass
|
||||||
|
else
|
||||||
|
echo " ✗ HTTPS 反向代理响应体 — 未包含后端内容"
|
||||||
|
fail
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 清理 HTTPS 后端服务器
|
||||||
|
kill -9 $BACKEND_HTTPS_PID 2>/dev/null || true
|
||||||
|
|
||||||
# 恢复默认服务器
|
# 恢复默认服务器
|
||||||
kill_server
|
kill_server
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user