Files
wehub-resource-sync 1b8708893a
Security Scan / tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:12:26 +08:00

106 lines
2.2 KiB
Go

package mitm
import (
"bufio"
"crypto/tls"
"fmt"
"net/http"
"strconv"
"strings"
)
// connResponseWriter is a minimal HTTP/1.1 http.ResponseWriter
// that writes directly to a hijacked TLS connection.
type connResponseWriter struct {
conn *tls.Conn
bw *bufio.Writer
req *http.Request
header http.Header
wroteHeader bool
chunked bool
contentLength int64
written int64
closeAfter bool
}
func newConnResponseWriter(conn *tls.Conn, req *http.Request) *connResponseWriter {
return &connResponseWriter{
conn: conn,
bw: bufio.NewWriter(conn),
req: req,
header: make(http.Header),
contentLength: -1,
}
}
func (w *connResponseWriter) Header() http.Header { return w.header }
func (w *connResponseWriter) WriteHeader(status int) {
if w.wroteHeader {
return
}
w.wroteHeader = true
if cl := w.header.Get("Content-Length"); cl != "" {
if n, err := strconv.ParseInt(cl, 10, 64); err == nil {
w.contentLength = n
}
}
if w.contentLength < 0 {
w.chunked = true
w.header.Set("Transfer-Encoding", "chunked")
w.header.Del("Content-Length")
}
// "Connection: close" is case-insensitive per RFC 9110 §7.6.1; some
// upstreams send "Close" or "CLOSE". Use EqualFold so any casing
// triggers the post-response disconnect.
for _, v := range w.header.Values("Connection") {
if strings.EqualFold(v, "close") {
w.closeAfter = true
}
}
_, _ = fmt.Fprintf(w.bw, "HTTP/1.1 %d %s\r\n", status, http.StatusText(status))
_ = w.header.Write(w.bw)
_, _ = w.bw.WriteString("\r\n")
}
func (w *connResponseWriter) Write(p []byte) (int, error) {
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
if w.chunked {
if _, err := fmt.Fprintf(w.bw, "%x\r\n", len(p)); err != nil {
return 0, err
}
n, err := w.bw.Write(p)
if err != nil {
return n, err
}
if _, err := w.bw.WriteString("\r\n"); err != nil {
return n, err
}
w.written += int64(n)
return n, nil
}
n, err := w.bw.Write(p)
w.written += int64(n)
return n, err
}
func (w *connResponseWriter) Flush() {
_ = w.bw.Flush()
}
func (w *connResponseWriter) finish() {
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
if w.chunked {
_, _ = w.bw.WriteString("0\r\n\r\n")
}
_ = w.bw.Flush()
}