diff --git a/assets/http-proxy-err.html b/assets/http-proxy-err.html index 81992f1..8443716 100644 --- a/assets/http-proxy-err.html +++ b/assets/http-proxy-err.html @@ -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); diff --git a/http.go b/http.go index c7aee98..99af21a 100644 --- a/http.go +++ b/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")