e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
224 lines
5.4 KiB
Go
224 lines
5.4 KiB
Go
// Copyright 2025 Alibaba Group Holding Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package proxy
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
slogger "github.com/alibaba/opensandbox/internal/logger"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func Test_WebSocketProxy(t *testing.T) {
|
|
t.Run("with header mode", func(t *testing.T) {
|
|
webSocketProxyWithHeaderMode(t)
|
|
})
|
|
t.Run("with uri mode", func(t *testing.T) {
|
|
webSocketProxyWithURIMode(t)
|
|
})
|
|
}
|
|
|
|
func webSocketProxyWithHeaderMode(t *testing.T) {
|
|
// Create mock provider
|
|
provider := &mockProvider{
|
|
endpoints: map[string]string{
|
|
"test-sandbox": "127.0.0.1",
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
Logger = slogger.MustNew(slogger.Config{Level: "debug"})
|
|
proxy := NewProxy(ctx, provider, ModeHeader, nil, nil)
|
|
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/", proxy)
|
|
proxyPort, err := findAvailablePort()
|
|
proxyURL := "ws://127.0.0.1:" + strconv.Itoa(proxyPort)
|
|
assert.Nil(t, err)
|
|
|
|
go func() {
|
|
assert.NoError(t, http.ListenAndServe(":"+strconv.Itoa(proxyPort), mux))
|
|
}()
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
backendPort, err := findAvailablePort()
|
|
assert.Nil(t, err)
|
|
|
|
// backend echo server
|
|
go func() {
|
|
mux2 := http.NewServeMux()
|
|
mux2.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
|
t.Logf("r.URL.Path: %s", r.URL.Path)
|
|
t.Logf("r.URL.RawPath: %s", r.URL.RawPath)
|
|
t.Logf("r.Host: %s", r.Host)
|
|
// Don't upgrade if original host header isn't preserved
|
|
assert.True(t, strings.HasPrefix(r.Host, "127.0.0.1"))
|
|
|
|
conn, err := defaultUpgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
|
|
messageType, p, err := conn.ReadMessage()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if err = conn.WriteMessage(messageType, p); err != nil {
|
|
return
|
|
}
|
|
})
|
|
|
|
err := http.ListenAndServe(":"+strconv.Itoa(backendPort), mux2)
|
|
if err != nil {
|
|
t.Error("ListenAndServe: ", err)
|
|
return
|
|
}
|
|
}()
|
|
|
|
time.Sleep(time.Millisecond * 100)
|
|
|
|
// frontend server, dial now our proxy, which will reverse proxy our
|
|
// message to the backend websocket server.
|
|
h := http.Header{}
|
|
h.Set(SandboxIngress, "test-sandbox-"+strconv.Itoa(backendPort))
|
|
conn, _, err := websocket.DefaultDialer.Dial(proxyURL+"/ws", h)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// write a message and send it to the backend server
|
|
msg := "hello kite"
|
|
err = conn.WriteMessage(websocket.TextMessage, []byte(msg))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
messageType, p, err := conn.ReadMessage()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if messageType != websocket.TextMessage {
|
|
t.Error("incoming message type is not Text")
|
|
}
|
|
|
|
if msg != string(p) {
|
|
t.Errorf("expecting: %s, got: %s", msg, string(p))
|
|
}
|
|
}
|
|
|
|
func webSocketProxyWithURIMode(t *testing.T) {
|
|
// Create mock provider
|
|
provider := &mockProvider{
|
|
endpoints: map[string]string{
|
|
"test-sandbox": "127.0.0.1",
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
Logger = slogger.MustNew(slogger.Config{Level: "debug"})
|
|
proxy := NewProxy(ctx, provider, ModeURI, nil, nil)
|
|
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/", proxy)
|
|
proxyPort, err := findAvailablePort()
|
|
proxyURL := "ws://127.0.0.1:" + strconv.Itoa(proxyPort)
|
|
assert.Nil(t, err)
|
|
|
|
go func() {
|
|
assert.NoError(t, http.ListenAndServe(":"+strconv.Itoa(proxyPort), mux))
|
|
}()
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
backendPort, err := findAvailablePort()
|
|
assert.Nil(t, err)
|
|
|
|
// backend echo server
|
|
go func() {
|
|
mux2 := http.NewServeMux()
|
|
mux2.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
|
t.Logf("r.URL.Path: %s", r.URL.Path)
|
|
t.Logf("r.URL.RawPath: %s", r.URL.RawPath)
|
|
t.Logf("r.Host: %s", r.Host)
|
|
// Don't upgrade if original host header isn't preserved
|
|
assert.True(t, strings.HasPrefix(r.Host, "127.0.0.1"))
|
|
|
|
conn, err := defaultUpgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
|
|
messageType, p, err := conn.ReadMessage()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if err = conn.WriteMessage(messageType, p); err != nil {
|
|
return
|
|
}
|
|
})
|
|
|
|
err := http.ListenAndServe(":"+strconv.Itoa(backendPort), mux2)
|
|
if err != nil {
|
|
t.Error("ListenAndServe: ", err)
|
|
return
|
|
}
|
|
}()
|
|
|
|
time.Sleep(time.Millisecond * 100)
|
|
|
|
// frontend server, dial now our proxy, which will reverse proxy our
|
|
// message to the backend websocket server.
|
|
h := http.Header{}
|
|
h.Set(SandboxIngress, "test-sandbox-"+strconv.Itoa(backendPort))
|
|
conn, _, err := websocket.DefaultDialer.Dial(proxyURL+fmt.Sprintf("/test-sandbox/%v", backendPort)+"/ws", h)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// write a message and send it to the backend server
|
|
msg := "hello kite"
|
|
err = conn.WriteMessage(websocket.TextMessage, []byte(msg))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
messageType, p, err := conn.ReadMessage()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if messageType != websocket.TextMessage {
|
|
t.Error("incoming message type is not Text")
|
|
}
|
|
|
|
if msg != string(p) {
|
|
t.Errorf("expecting: %s, got: %s", msg, string(p))
|
|
}
|
|
}
|