mirror of
https://github.com/zhaojh329/rttys.git
synced 2026-02-27 09:53:21 +08:00
refactor http proxy request handling and improve performance
This commit significantly optimizes the HTTP proxy implementation by: 1. Replacing standard http.ReadRequest with manual HTTP header parsing - Avoids unnecessary allocations from full request parsing - Adds 3-second timeout for initial header reading 2. Removing HttpProxyWriter abstraction - Directly construct and send rewritten Host header - Simplify data forwarding logic 3. Unifying WebSocket and regular HTTP handling - Use single read loop for both cases - Always use buffer pool for reads 4. Adding proper timeouts - Set deadlines for header reading - Reset timeout after headers are processed These changes reduce memory allocations, improve performance, and simplify the proxy logic. Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
This commit is contained in:
@@ -92,18 +92,14 @@
|
||||
const translations = {
|
||||
en: {
|
||||
'Device Unavailable': 'Device Unavailable',
|
||||
'Invalid Request': 'Invalid Request',
|
||||
'Unauthorized Access': 'Unauthorized Access',
|
||||
'Device offline message': 'The device is currently offline. Please check the device status and try again.',
|
||||
'Invalid request message': 'The request is invalid or malformed',
|
||||
'Unauthorized request message': 'You are not authorized to access this resource. Please check your session and try again.'
|
||||
},
|
||||
'zh-CN': {
|
||||
'Device Unavailable': '设备不可用',
|
||||
'Invalid Request': '无效请求',
|
||||
'Unauthorized Access': '未授权访问',
|
||||
'Device offline message': '设备当前离线,请检查设备状态后重试。',
|
||||
'Invalid request message': '请求无效或格式错误',
|
||||
'Unauthorized request message': '您无权访问此资源。请检查您的会话并重试。'
|
||||
}
|
||||
};
|
||||
@@ -123,10 +119,6 @@
|
||||
title = t('Device Unavailable', lang);
|
||||
message = t('Device offline message', lang);
|
||||
break;
|
||||
case 'invalid':
|
||||
title = t('Invalid Request', lang);
|
||||
message = t('Invalid request message', lang);
|
||||
break;
|
||||
case 'unauthorized':
|
||||
title = t('Unauthorized Access', lang);
|
||||
message = t('Unauthorized request message', lang);
|
||||
|
||||
157
http.go
157
http.go
@@ -18,6 +18,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -120,30 +121,16 @@ func doHttpProxy(srv *RttyServer, c net.Conn) {
|
||||
defer logPanic()
|
||||
defer c.Close()
|
||||
|
||||
head := bytebufferpool.Get()
|
||||
defer bytebufferpool.Put(head)
|
||||
|
||||
br := bufio.NewReader(c)
|
||||
|
||||
req, err := http.ReadRequest(br)
|
||||
if err != nil {
|
||||
ses := httpReadRequest(c, br, head)
|
||||
if ses == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cookie, err := req.Cookie("rtty-http-sid")
|
||||
if err != nil {
|
||||
log.Debug().Msgf(`not found cookie "rtty-http-sid"`)
|
||||
sendHTTPErrorResponse(c, "invalid")
|
||||
return
|
||||
}
|
||||
sid := cookie.Value
|
||||
|
||||
sesVal, ok := httpProxySessions.Load(sid)
|
||||
if !ok {
|
||||
log.Debug().Msgf(`not found httpProxySession "%s"`, sid)
|
||||
sendHTTPErrorResponse(c, "unauthorized")
|
||||
return
|
||||
}
|
||||
|
||||
ses := sesVal.(*HttpProxySession)
|
||||
|
||||
dev := srv.GetDevice(ses.group, ses.devid)
|
||||
if dev == nil {
|
||||
log.Debug().Msgf(`device "%s" group "%s" offline`, ses.devid, ses.group)
|
||||
@@ -151,16 +138,11 @@ func doHttpProxy(srv *RttyServer, c net.Conn) {
|
||||
return
|
||||
}
|
||||
|
||||
destAddr, hostHeaderRewrite := genDestAddrAndHost(ses.destaddr, ses.https)
|
||||
destAddr, host := genDestAddrAndHost(ses.destaddr, ses.https)
|
||||
|
||||
hpw := &HttpProxyWriter{
|
||||
destAddr: destAddr,
|
||||
hostHeaderRewrite: hostHeaderRewrite,
|
||||
dev: dev,
|
||||
https: ses.https,
|
||||
}
|
||||
var srcAddr [18]byte
|
||||
|
||||
tcpAddr2Bytes(c.RemoteAddr().(*net.TCPAddr), hpw.srcAddr[:])
|
||||
tcpAddr2Bytes(c.RemoteAddr().(*net.TCPAddr), srcAddr[:])
|
||||
|
||||
ctx, cancel := context.WithCancel(ses.ctx)
|
||||
defer cancel()
|
||||
@@ -169,38 +151,101 @@ func doHttpProxy(srv *RttyServer, c net.Conn) {
|
||||
<-ctx.Done()
|
||||
c.Close()
|
||||
log.Debug().Msgf("http proxy conn closed: %s", ses)
|
||||
dev.https.Delete(hpw.srcAddr)
|
||||
dev.https.Delete(srcAddr)
|
||||
}()
|
||||
|
||||
log.Debug().Msgf("new http proxy conn: %s", ses)
|
||||
|
||||
dev.https.Store(hpw.srcAddr, c)
|
||||
dev.https.Store(srcAddr, c)
|
||||
|
||||
req.Host = hostHeaderRewrite
|
||||
hpw.WriteRequest(req)
|
||||
// Rewrite the HTTP host
|
||||
head.WriteString("Host: " + host + "\r\n")
|
||||
|
||||
if req.Header.Get("Upgrade") == "websocket" {
|
||||
hb := httpBufPool.Get().(*HttpBuf)
|
||||
defer httpBufPool.Put(hb)
|
||||
sendHttpReq(dev, ses.https, srcAddr[:], destAddr, head.B)
|
||||
|
||||
for {
|
||||
n, err := c.Read(hb.buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sendHttpReq(dev, ses.https, hpw.srcAddr[:], destAddr, hb.buf[:n])
|
||||
ses.Expire()
|
||||
hb := httpBufPool.Get().(*HttpBuf)
|
||||
defer httpBufPool.Put(hb)
|
||||
|
||||
for {
|
||||
n, err := br.Read(hb.buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
for {
|
||||
req, err := http.ReadRequest(br)
|
||||
sendHttpReq(dev, ses.https, srcAddr[:], destAddr, hb.buf[:n])
|
||||
ses.Expire()
|
||||
}
|
||||
}
|
||||
|
||||
func httpReadRequest(conn net.Conn, br *bufio.Reader, head *bytebufferpool.ByteBuffer) *HttpProxySession {
|
||||
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
|
||||
// First line: GET /index.html HTTP/1.0
|
||||
line, err := br.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
head.WriteString(line)
|
||||
|
||||
sid := ""
|
||||
|
||||
for {
|
||||
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
|
||||
line, err := br.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if line == "\r\n" {
|
||||
break
|
||||
}
|
||||
|
||||
k, v, ok := strings.Cut(line, ":")
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
k = strings.ToLower(k)
|
||||
v = strings.TrimLeft(v, " \t")
|
||||
|
||||
if k == "host" {
|
||||
continue
|
||||
}
|
||||
|
||||
head.WriteString(line)
|
||||
|
||||
if k == "cookie" {
|
||||
cookies, err := http.ParseCookie(v)
|
||||
if err != nil {
|
||||
return
|
||||
break
|
||||
}
|
||||
hpw.WriteRequest(req)
|
||||
ses.Expire()
|
||||
|
||||
for _, cookie := range cookies {
|
||||
if cookie.Name == "rtty-http-sid" {
|
||||
sid = cookie.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sid == "" {
|
||||
log.Debug().Msgf(`not found cookie "rtty-http-sid"`)
|
||||
sendHTTPErrorResponse(conn, "unauthorized")
|
||||
return nil
|
||||
}
|
||||
|
||||
ses, ok := httpProxySessions.Load(sid)
|
||||
if !ok {
|
||||
log.Debug().Msgf(`not found httpProxySession "%s"`, sid)
|
||||
sendHTTPErrorResponse(conn, "unauthorized")
|
||||
return nil
|
||||
}
|
||||
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
|
||||
return ses.(*HttpProxySession)
|
||||
}
|
||||
|
||||
func httpProxyRedirect(a *APIServer, c *gin.Context, group string) {
|
||||
@@ -382,24 +427,6 @@ func httpProxyVaildAddr(addr string, https bool) (net.IP, uint16, error) {
|
||||
return ip, uint16(port), nil
|
||||
}
|
||||
|
||||
type HttpProxyWriter struct {
|
||||
destAddr []byte
|
||||
srcAddr [18]byte
|
||||
hostHeaderRewrite string
|
||||
dev *Device
|
||||
https bool
|
||||
}
|
||||
|
||||
func (rw *HttpProxyWriter) Write(p []byte) (n int, err error) {
|
||||
sendHttpReq(rw.dev, rw.https, rw.srcAddr[:], rw.destAddr, p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (rw *HttpProxyWriter) WriteRequest(req *http.Request) {
|
||||
req.Host = rw.hostHeaderRewrite
|
||||
req.Write(rw)
|
||||
}
|
||||
|
||||
func sendHTTPErrorResponse(conn net.Conn, errorType string) {
|
||||
fs, _ := fs.Sub(staticFs, "assets")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user