toukautil

package module
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2025 License: Apache-2.0 Imports: 23 Imported by: 0

README

toukautil

提供基于Touka框架的实用实现, 包括反向代理等

Documentation

Index

Constants

View Source
const UnDefiendRateString = "0"
View Source
const UnDefinedRateString = "0" // 无效/未定义速率的字符串表示
View Source
const UnlimitedRateString = "-1"

Variables

This section is empty.

Functions

func BandwidthLimit

func BandwidthLimit(opts BandwidthLimiterOptions) touka.HandlerFunc

BandwidthLimit 返回一个 Touka 中间件,用于限制请求体上传和响应体下载的带宽。 它会同时考虑通过 SetGlobalRateLimit/SetGlobalWriteRateLimit 设置的全局限制 以及通过 BandwidthLimiterOptions 配置的针对每个 key 的独立限制。

func DirectSingleReverseProxy

func DirectSingleReverseProxy(targetURL string, tempConfig *ReverseProxyConfig, c *touka.Context)

DirectSingleReverseProxy 提供了一个简单的接口来反向代理到一个完整的 URL, 并且允许传入一个临时的 ReverseProxyConfig 来覆盖默认配置 与 SimpleSingleReverseProxy 不同,它不使用完全默认配置,而是基于传入的 tempConfig 同时,它设置 config.isDirectUrl = true,表示 targetURL 参数就是完整的后端 URL, 不需要再与原始请求路径拼接

示例用法:

r.GET("/direct-proxy", func(c *touka.Context) {
    targetURL := c.Query("target") // 从查询参数获取完整的后端 URL
    if targetURL == "" {
        c.String(http.StatusBadRequest, "Missing target URL")
        return
    }
    // 可以在这里设置一些临时的配置,例如超时时间
    tempConfig := toukautil.ReverseProxyConfig{
        HTTPClient: &http.Client{ Timeout: 10 * time.Second },
    }
    toukautil.DirectSingleReverseProxy(targetURL, &tempConfig, c)
})

func HandleReverseProxy

func HandleReverseProxy(c *touka.Context, config ReverseProxyConfig)

HandleReverseProxy 实现了一个便捷的 Touka Handler,用于将请求完整地反向代理 它内部调用 ServeReverseProxy,并使用 config 中定义的 TargetURL 这个函数适用于在定义 Touka 路由时,直接指定一个完整的反向代理行为

示例用法:

r.GET("/proxy-to-service-a/*path", func(c *touka.Context) {
    config := toukautil.ReverseProxyConfig{ TargetURL: "http://service-a-backend" }
    toukautil.HandleReverseProxy(c, config)
})

func ParseRate

func ParseRate(rateStr string) (rate.Limit, error)

ParseRate 解析人类可读的速度字符串 (例如, "100kbps", "1.5MB/s", "5000")。 返回速率,单位是每秒字节数 (rate.Limit)。 如果 rateStr 为 "-1" (不区分大小写,忽略空格),则返回 rate.Inf 表示无限速。 如果解析结果为非正数 (且不是 "-1"),则返回错误。

func ServeReverseProxy

func ServeReverseProxy(c *touka.Context, config ReverseProxyConfig, dynamicTargetURL ...string)

ServeReverseProxy ("自动模式") 对给定的 Touka Context 执行完整的反向代理操作 它处理从构建请求到将响应体完全复制回客户端的所有步骤,包括错误处理和中间件

func ServeReverseProxyCore

func ServeReverseProxyCore(c *touka.Context, config ReverseProxyConfig, target *url.URL, httpClient *httpc.Client) (backendResp *http.Response, err error)

ServeReverseProxyCore 是反向代理的核心逻辑,不直接处理响应体的复制 它负责: 1. 构建出站请求 (使用 httpc.RequestBuilder) 2. 应用 RewriteRequest 回调 3. 发送请求到后端 (使用 httpc.Client) 4. 处理 HTTP 1xx (Continue) 响应 5. 如果是协议升级 (如 WebSocket),调用 handleProtocolUpgrade 6. 对于普通响应,移除逐跳头部并应用 ModifyResponse 回调

返回后端响应对象 `*http.Response`(Body 尚未关闭,由调用者负责)和任何发生的错误 此函数更偏向于“手动模式”的构建块

func ServeReverseProxyManual

func ServeReverseProxyManual(c *touka.Context, config ReverseProxyConfig, dynamicTargetURL ...string) (processedBackendHeader http.Header, backendResp *http.Response, err error)

ServeReverseProxyManual ("手动模式") 允许调用者更细致地控制反向代理的响应处理 它执行代理请求的核心逻辑 (通过 ServeReverseProxyCore),处理协议升级, 并返回处理后的后端响应头和整个后端响应对象

返回值:

  • processedBackendHeader (http.Header): 这是从后端响应中提取并处理过(移除了逐跳头部,应用了ModifyResponse)的头部 如果发生错误导致无法获取后端响应,此值可能为 nil
  • backendResp (*http.Response): 从后端获取的原始(或已由ModifyResponse修改的)*http.Response 对象 调用者【始终负责】在处理完毕后调用 backendResp.Body.Close() 来释放资源,即使在发生错误时也是如此(如果 backendResp 非 nil)
  • err (error): 代理过程中发生的任何错误

调用者责任: 1. 检查返回的 `err` 2. 如果 `backendResp` 非 `nil`,必须调用 `backendResp.Body.Close()` 3. 根据 `processedBackendHeader` 和 `backendResp` (特别是 `backendResp.Body`) 自行构建对客户端的响应

协议升级: 如果 `err` 为 `nil` 且 `backendResp.StatusCode` 是 `http.StatusSwitchingProtocols` (101), 则表示协议升级已由 `ServeReverseProxyCore` 内部的 `handleProtocolUpgrade` 处理完毕 `backendResp.Body` 将是与后端服务器的劫持连接 (`io.ReadWriteCloser`) 调用者此时通常不需要再对客户端做 HTTP 响应,因为连接已被劫持用于新协议 调用者仍需关闭这个 `backendResp.Body` (劫持的连接) 当它不再需要时

func SetGlobalRateLimit

func SetGlobalRateLimit(limit rate.Limit, burst int)

SetGlobalRateLimit 设置全局读取速率限制。 limit: 全局速率限制,单位是 Bytes/s (rate.Limit)。rate.Inf 表示无限制。 burst: 全局令牌桶的突发容量,单位是字节。 将 limit 设置为 <= 0 或 rate.Inf 将禁用全局限速。 !! 重要提示: 此函数应仅在应用程序初始化期间调用。 !! 在 RateLimitedReader 实例创建后更改全局限制将不会反映在这些实例的缓存状态中。

func SetGlobalWriteRateLimit

func SetGlobalWriteRateLimit(limit rate.Limit, burst int)

SetGlobalWriteRateLimit 设置全局写入速率限制。 limit: 全局写入速率限制,单位是 Bytes/s (rate.Limit)。rate.Inf 表示无限制。 burst: 全局写入令牌桶的突发容量,单位是字节。 将 limit 设置为 <= 0 或 rate.Inf 将禁用全局写入限速。 重要提示: 此函数应主要在应用程序初始化期间调用。 在 RateLimitedWriter 实例创建后更改全局限制可能不会反映在这些实例的缓存状态中。

func SimpleSingleReverseProxy

func SimpleSingleReverseProxy(targetURL string, c *touka.Context)

SimpleSingleReverseProxy 提供了一个最简单的接口来反向代理到一个完整的 URL 它使用默认的配置,只要求提供目标 URL 和 Touka Context 适用于快速、无特殊配置的单个 URL 代理场景

示例用法:

r.GET("/quick-proxy/*path", func(c *touka.Context) {
    toukautil.SimpleSingleReverseProxy("http://another-service.com", c)
})

func SingleReverseProxy

func SingleReverseProxy(targetURL string, c *touka.Context, config ReverseProxyConfig)

SingleReverseProxy 将当前 Touka Context 中的请求反向代理到指定的 `targetURL` 它使用传入的 `config` 作为基础配置,但 `targetURL` 参数会覆盖 `config.TargetURL` 这个函数适用于那些目标 URL 需要在运行时动态确定的场景, 例如,基于请求的某些参数或头部来选择不同的后端服务

示例用法:

r.GET("/dynamic-proxy", func(c *touka.Context) {
    targetService := c.Query("service_id") // 从查询参数获取目标
    targetServiceURL := "http://backend-" + targetService + ".internal"
    baseConfig := toukautil.ReverseProxyConfig{ /* ... 一些通用配置 ... */ }
    toukautil.SingleReverseProxy(targetServiceURL, c, baseConfig)
})

Types

type BandwidthLimiterOptions

type BandwidthLimiterOptions struct {
	// UploadLimit 是针对每个 key 的上传(请求体读取)速率限制 (Bytes/s)。
	// rate.Inf 表示无独立上传限制。
	UploadLimit rate.Limit
	// UploadBurst 是上传令牌桶的突发容量 (bytes)。
	UploadBurst int

	// DownloadLimit 是针对每个 key 的下载(响应体写入)速率限制 (Bytes/s)。
	// rate.Inf 表示无独立下载限制。
	DownloadLimit rate.Limit
	// DownloadBurst 是下载令牌桶的突发容量 (bytes)。
	DownloadBurst int

	// KeyFunc 用于从 Touka Context 中为每个请求提取一个唯一的字符串 key。
	// 上传和下载将使用此 key 应用独立的速率限制器。
	// 如果为 nil,默认不进行基于 key 的独立限速(只受全局限速影响,如果全局限速开启)。
	// 如果希望基于IP限速,可以设置为: func(c *touka.Context) string { return c.ClientIP() }
	KeyFunc func(c *touka.Context) string

	// Skip 是一个可选函数,如果返回 true,则当前请求将跳过所有带宽限制检查。
	Skip func(c *touka.Context) bool
}

BandwidthLimiterOptions 用于配置带宽限制中间件。

type RateLimitedReader

type RateLimitedReader struct {
	// contains filtered or unexported fields
}

RateLimitedReader 包装一个 io.Reader,并应用速率限制。 它同时受自身独立限速器和全局限速器的约束。

func NewRateLimitedReader

func NewRateLimitedReader(r io.Reader, limit rate.Limit, burst int, ctx context.Context) *RateLimitedReader

NewRateLimitedReader 创建一个新的 RateLimitedReader。 r: 底层的读取器 (如: resp.Body)。 limit: 独立速率限制,单位是 Bytes/s (rate.Limit)。rate.Inf 表示无限制。 burst: 独立令牌桶的突发容量,单位是字节。 ctx: 与操作关联的 Context (如: 请求 Context)。 将 limit 设置为 <= 0 或 rate.Inf 将禁用此读取器的独立限速。 全局限制的激活状态是基于调用此函数时全局限制的状态确定的。 !! 重要提示: 此 RateLimitedReader 的全局限速行为将固定为创建此实例时的全局状态。

func (*RateLimitedReader) Close

func (rlr *RateLimitedReader) Close() error

Close 实现 io.Closer 接口,转发 Close 调用给底层 Reader。

func (*RateLimitedReader) Read

func (rlr *RateLimitedReader) Read(p []byte) (n int, err error)

Read 实现 io.Reader 接口。 在读取数据之前,根据缓存的状态决定是否需要向限速器申请许可。

type RateLimitedWriter

type RateLimitedWriter struct {
	// contains filtered or unexported fields
}

RateLimitedWriter 包装一个 io.Writer (通常是 http.ResponseWriter),并对其写入操作应用速率限制。 它同时受其自身配置的独立限速器和(如果激活)全局写入限速器的约束。

func NewRateLimitedWriter

func NewRateLimitedWriter(w io.Writer, limit rate.Limit, burst int, ctx context.Context) *RateLimitedWriter

NewRateLimitedWriter 创建一个新的 RateLimitedWriter 实例。 w: 底层的 io.Writer。 limit: 此写入器的独立速率限制,单位 Bytes/s (rate.Limit)。rate.Inf 表示无独立限制。 burst: 此写入器的独立令牌桶突发容量,单位字节。 ctx: 与写入操作关联的 Context,用于在等待令牌时支持取消。

注意: 全局写入限速器的状态是在调用此构造函数时确定的,并缓存。 如果在 RateLimitedWriter 实例存在期间通过 SetGlobalWriteRateLimit 更改全局限制, 已创建的实例将继续使用其创建时缓存的全局限速状态。

func (*RateLimitedWriter) Close

func (rlw *RateLimitedWriter) Close() error

Close 实现 io.Closer 接口 (如果底层写入器支持)。 它会将 Close 调用传递给包装的 io.Writer (如果它实现了 io.Closer)。

func (*RateLimitedWriter) Flush

func (rlw *RateLimitedWriter) Flush()

Flush 实现 http.Flusher 接口 (如果底层写入器支持)。 这对于像 http.ResponseWriter 这样的写入器很重要,可以确保数据被发送到客户端。 注意:Flush 操作本身不应该被速率限制,因为它只是刷新已写入的缓冲数据。 速率限制应用于 Write 操作。

func (*RateLimitedWriter) Header

func (rlw *RateLimitedWriter) Header() http.Header

Header 实现 http.ResponseWriter 接口的部分功能 (如果底层写入器是 http.ResponseWriter)。 这使得 RateLimitedWriter 在包装 http.ResponseWriter 时能更透明。

func (*RateLimitedWriter) Hijack

func (rlw *RateLimitedWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)

Hijack 实现 http.Hijacker 接口 (如果底层写入器支持)。 对于需要接管连接的场景 (如 WebSocket),代理或包装器需要支持 Hijack。 速率限制不适用于劫持后的连接,因为协议已改变。

func (*RateLimitedWriter) IsHijacked

func (rlw *RateLimitedWriter) IsHijacked() bool

IsHijacked 返回连接是否已被劫持。 它尝试调用底层 Writer 的 IsHijacked() 方法。

func (*RateLimitedWriter) Size

func (rlw *RateLimitedWriter) Size() int

Size 返回已写入响应体的字节数。 它尝试调用底层 Writer 的 Size() 方法。 注意:这个 Size 反映的是通过底层 Writer 实际写入的字节数, RateLimitedWriter 本身不直接修改或计算这个值。

func (*RateLimitedWriter) Status

func (rlw *RateLimitedWriter) Status() int

Status 返回写入的 HTTP 状态码。 它尝试调用底层 Writer 的 Status() 方法(如果它是 touka.ResponseWriter)。 否则,它无法确定状态码,可以返回0或一个默认值。

func (*RateLimitedWriter) Write

func (rlw *RateLimitedWriter) Write(p []byte) (n int, err error)

Write 实现 io.Writer 接口。 它在将数据写入底层写入器之前,根据配置的速率限制等待必要的令牌。 写入的字节数 (n) 用于从限速器请求相应数量的令牌。

func (*RateLimitedWriter) WriteHeader

func (rlw *RateLimitedWriter) WriteHeader(statusCode int)

WriteHeader 实现 http.ResponseWriter 接口的部分功能。

func (*RateLimitedWriter) Written

func (rlw *RateLimitedWriter) Written() bool

Written 返回 WriteHeader 是否已被调用。 它尝试调用底层 Writer 的 Written() 方法。

type ResponseMiddleware

type ResponseMiddleware func(backendResp *http.Response, clientResWriter http.ResponseWriter, c *touka.Context) error

ResponseMiddleware 是一个在后端响应头已写入客户端,但在响应体复制之前执行的中间件类型 它允许在最后时刻修改客户端响应(主要是头部)或执行其他操作 如果中间件返回错误,将中止响应体的复制,并调用配置的 ErrorHandler

type ReverseProxyConfig

type ReverseProxyConfig struct {
	// TargetURL 是后端目标服务器的基础 URL 字符串
	// 例如 "http://localhost:8081/api" 或 "https://service.example.com"
	// 对于 HandleReverseProxy,此字段会被使用
	// 对于其他接受 targetURL 参数的函数,此字段通常会被覆盖或忽略
	TargetURL string

	// HTTPClient 是用于执行出站请求的 httpc.Client 实例
	// 如果为 nil,将尝试从 touka.Context 的 c.GetHTTPC() 中获取
	HTTPClient *httpc.Client

	// RewriteRequest 是一个可选的回调函数,在发送请求到后端之前修改出站请求
	// 参数 inReq 是原始的入站请求 (来自客户端)
	// 参数 outReqBuilder 是 httpc.RequestBuilder,用于构建出站请求
	// 如果此函数返回错误,代理将中止并调用 ErrorHandler (在自动模式下) 或返回错误 (在手动模式下)
	RewriteRequest func(inReq *http.Request, outReqBuilder *httpc.RequestBuilder) error

	// ModifyResponse 是一个可选的回调函数,在收到后端响应后、
	// 将其头部和状态码应用到客户端响应之前调用 (在自动模式下)
	// 或在返回给手动模式调用者之前调用
	// 允许检查或修改将要写回客户端的响应(主要是头部)
	// 如果此函数返回错误,代理将调用 ErrorHandler (在自动模式下) 或返回错误 (在手动模式下)
	ModifyResponse func(backendResp *http.Response) error

	// ResponseMiddlewares 是一个响应处理中间件切片
	// 它们在后端响应头和状态码已写入客户端之后、响应体开始复制之前按顺序执行
	// 注意: 此选项仅在 ServeReverseProxy (自动模式) 中生效
	ResponseMiddlewares []ResponseMiddleware

	// ErrorHandler 是一个可选函数,用于处理代理过程中发生的错误
	// 如果为 nil,将使用默认的错误处理
	// 此处理器负责向客户端发送错误响应并中止 Touka Context (如果需要)
	ErrorHandler func(c *touka.Context, err error)

	// FlushInterval 指定在复制响应体时刷新到客户端的刷新间隔
	// 零表示不周期性刷新负值表示每次写入后立即刷新
	// 对于流式响应 (ContentLength -1 或 Content-Type text/event-stream) 会被覆盖为立即刷新
	// 注意: 此选项仅在 ServeReverseProxy (自动模式) 中生效
	FlushInterval time.Duration
	// contains filtered or unexported fields
}

ReverseProxyConfig 包含反向代理的配置选项

type UnDefiendRateStringErr

type UnDefiendRateStringErr struct {
	// contains filtered or unexported fields
}

定义特殊error UnDefiendRateStringErr

func (*UnDefiendRateStringErr) Error

func (e *UnDefiendRateStringErr) Error() string

type UnDefinedRateErr

type UnDefinedRateErr struct {
	// contains filtered or unexported fields
}

UnDefinedRateErr 表示速率字符串为 "0",这被认为是一个无效的定义。 无限速率应使用 "-1"。

func (*UnDefinedRateErr) Error

func (e *UnDefinedRateErr) Error() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL