0e96f602fa
matcher: fix IPv6 normalization via netip.ParseAddr, port-range overwrite by switching to []*PortRange map, case-insensitive domain/host matching, glob.MustCompile→Compile to avoid panic on invalid patterns. plugin: guard nil *Options in NewGRPCConn and NewHTTPClient; add HTTPClientTransport helper for idle-connection cleanup. net/*: add doc comments for all exported symbols (no code changes). Add 30 tests (matcher_test.go 22, plugin_test.go 8) covering all matcher types, nil safety, case insensitivity, IPv6 normalization, port ranges, and plugin option composition.
79 lines
1.5 KiB
Go
79 lines
1.5 KiB
Go
package http
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// GetClientIP extracts the client IP from the request, checking CF-Connecting-IP,
|
|
// X-Forwarded-For, and X-Real-Ip headers in that order.
|
|
func GetClientIP(req *http.Request) net.IP {
|
|
if req == nil {
|
|
return nil
|
|
}
|
|
// cloudflare CDN
|
|
sip := req.Header.Get("CF-Connecting-IP")
|
|
if sip == "" {
|
|
ss := strings.Split(req.Header.Get("X-Forwarded-For"), ",")
|
|
if len(ss) > 0 && ss[0] != "" {
|
|
sip = strings.TrimSpace(ss[0])
|
|
}
|
|
}
|
|
if sip == "" {
|
|
sip = req.Header.Get("X-Real-Ip")
|
|
}
|
|
|
|
return net.ParseIP(sip)
|
|
}
|
|
|
|
// Body wraps an io.ReadCloser with size-limited recording of the data read.
|
|
type Body struct {
|
|
r io.ReadCloser
|
|
buf bytes.Buffer
|
|
length int64
|
|
recordSize int
|
|
}
|
|
|
|
// NewBody creates a Body that reads from r and records up to maxRecordSize bytes.
|
|
func NewBody(r io.ReadCloser, maxRecordSize int) *Body {
|
|
p := &Body{
|
|
r: r,
|
|
recordSize: maxRecordSize,
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
func (p *Body) Read(b []byte) (n int, err error) {
|
|
n, err = p.r.Read(b)
|
|
p.length += int64(n)
|
|
|
|
if p.recordSize > 0 {
|
|
b = b[:n]
|
|
if n > p.recordSize {
|
|
b = b[:p.recordSize]
|
|
}
|
|
p.buf.Write(b)
|
|
p.recordSize -= len(b)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (p *Body) Close() error {
|
|
return p.r.Close()
|
|
}
|
|
|
|
// Content returns the recorded content.
|
|
func (p *Body) Content() []byte {
|
|
return p.buf.Bytes()
|
|
}
|
|
|
|
// Length returns the total number of bytes read from the underlying reader.
|
|
func (p *Body) Length() int64 {
|
|
return p.length
|
|
}
|