Adds support to proxy WS requests on Hop (#1857)

Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
Daniel Valdivia
2022-04-17 23:16:12 -07:00
committed by GitHub
parent 991cc0953e
commit b36aed8845
8 changed files with 122 additions and 15 deletions

View File

@@ -29,6 +29,9 @@ import (
"net/http/cookiejar"
url2 "net/url"
"strings"
"time"
"github.com/gorilla/websocket"
v2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
@@ -181,11 +184,15 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
}
defer loginResp.Body.Close()
}
if tenantCookie == nil {
log.Println(errors.New("couldn't login to tenant and get cookie"))
responseWriter.WriteHeader(500)
responseWriter.WriteHeader(403)
return
}
// at this point we have a valid cookie ready to either route HTTP or WS
// now we need to know if we are doing an /api/ call (http) or /ws/ call (ws)
callType := urlParts[5]
targetURL, err := url2.Parse(tenantURL)
if err != nil {
@@ -206,7 +213,16 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
proxyCookieJar, _ := cookiejar.New(nil)
proxyCookieJar.SetCookies(targetURL, []*http.Cookie{proxiedCookie})
switch callType {
case "ws":
handleWSRequest(responseWriter, req, proxyCookieJar, targetURL, tenantSchema)
default:
handleHTTPRequest(responseWriter, req, proxyCookieJar, tenantBase, targetURL)
}
}
func handleHTTPRequest(responseWriter http.ResponseWriter, req *http.Request, proxyCookieJar *cookiejar.Jar, tenantBase string, targetURL *url2.URL) {
tr := &http.Transport{
// FIXME: use restapi.GetConsoleHTTPClient()
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
@@ -259,5 +275,75 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
responseWriter.WriteHeader(resp.StatusCode)
io.Copy(responseWriter, resp.Body)
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 0,
WriteBufferSize: 1024,
}
func handleWSRequest(responseWriter http.ResponseWriter, req *http.Request, proxyCookieJar *cookiejar.Jar, targetURL *url2.URL, schema string) {
dialer := &websocket.Dialer{
Proxy: http.ProxyFromEnvironment,
HandshakeTimeout: 45 * time.Second,
Jar: proxyCookieJar,
}
upgrader.CheckOrigin = func(r *http.Request) bool {
return true
}
c, err := upgrader.Upgrade(responseWriter, req, nil)
if err != nil {
log.Print("error upgrade connection:", err)
return
}
defer c.Close()
if schema == "http" {
targetURL.Scheme = "ws"
} else {
targetURL.Scheme = "wss"
}
// establish a websocket to the tenant
tenantConn, _, err := dialer.Dial(targetURL.String(), nil)
if err != nil {
log.Println("dial:", err)
return
}
defer tenantConn.Close()
doneTenant := make(chan struct{})
done := make(chan struct{})
// start read pump from tenant connection
go func() {
defer close(doneTenant)
for {
msgType, message, err := tenantConn.ReadMessage()
if err != nil {
log.Println("error read from tenant:", err)
return
}
c.WriteMessage(msgType, message)
}
}()
// start read pump from tenant connection
go func() {
defer close(done)
for {
msgType, message, err := c.ReadMessage()
if err != nil {
log.Println("error read from client:", err)
return
}
tenantConn.WriteMessage(msgType, message)
}
}()
select {
case <-done:
case <-doneTenant:
}
}

View File

@@ -188,9 +188,13 @@ const Heal = ({ classes, distributedSetup }: IHeal) => {
const isDev = process.env.NODE_ENV === "development";
const port = isDev ? "9090" : url.port;
// check if we are using base path, if not this always is `/`
const baseLocation = new URL(document.baseURI);
const baseUrl = baseLocation.pathname;
const wsProt = wsProtocol(url.protocol);
const c = new W3CWebSocket(
`${wsProt}://${url.hostname}:${port}/ws/heal/${bucketName}?prefix=${prefix}&recursive=${recursive}&force-start=${forceStart}&force-stop=${forceStop}`
`${wsProt}://${url.hostname}:${port}${baseUrl}ws/heal/${bucketName}?prefix=${prefix}&recursive=${recursive}&force-start=${forceStart}&force-stop=${forceStop}`
);
if (c !== null) {

View File

@@ -173,8 +173,12 @@ const HealthInfo = ({
const wsProt = wsProtocol(url.protocol);
// check if we are using base path, if not this always is `/`
const baseLocation = new URL(document.baseURI);
const baseUrl = baseLocation.pathname;
const c = new W3CWebSocket(
`${wsProt}://${url.hostname}:${port}/ws/health-info?deadline=1h`
`${wsProt}://${url.hostname}:${port}${baseUrl}ws/health-info?deadline=1h`
);
let interval: any | null = null;

View File

@@ -158,11 +158,14 @@ const ErrorLogs = ({
const port = isDev ? "9090" : url.port;
const wsProt = wsProtocol(url.protocol);
// check if we are using base path, if not this always is `/`
const baseLocation = new URL(document.baseURI);
const baseUrl = baseLocation.pathname;
c = new W3CWebSocket(
`${wsProt}://${
url.hostname
}:${port}/ws/console/?logType=${logType}&node=${
}:${port}${baseUrl}ws/console/?logType=${logType}&node=${
selectedNode === "Select node" ? "" : selectedNode
}`
);

View File

@@ -109,9 +109,13 @@ const Speedtest = ({ classes, distributedSetup }: ISpeedtest) => {
const isDev = process.env.NODE_ENV === "development";
const port = isDev ? "9090" : url.port;
// check if we are using base path, if not this always is `/`
const baseLocation = new URL(document.baseURI);
const baseUrl = baseLocation.pathname;
const wsProt = wsProtocol(url.protocol);
const c = new W3CWebSocket(
`${wsProt}://${url.hostname}:${port}/ws/speedtest?&size=${size}${sizeUnit}`
`${wsProt}://${url.hostname}:${port}${baseUrl}ws/speedtest?&size=${size}${sizeUnit}`
);
const baseDate = moment();

View File

@@ -166,12 +166,15 @@ const Trace = ({
if (all) {
calls = "all";
}
// check if we are using base path, if not this always is `/`
const baseLocation = new URL(document.baseURI);
const baseUrl = baseLocation.pathname;
const wsProt = wsProtocol(url.protocol);
c = new W3CWebSocket(
`${wsProt}://${
url.hostname
}:${port}/ws/trace?calls=${calls}&threshold=${threshold}&onlyErrors=${
}:${port}${baseUrl}ws/trace?calls=${calls}&threshold=${threshold}&onlyErrors=${
errors ? "yes" : "no"
}&statusCode=${statusCode}&method=${method}&funcname=${func}&path=${path}`
);

View File

@@ -128,9 +128,13 @@ const Watch = ({
const isDev = process.env.NODE_ENV === "development";
const port = isDev ? "9090" : url.port;
// check if we are using base path, if not this always is `/`
const baseLocation = new URL(document.baseURI);
const baseUrl = baseLocation.pathname;
const wsProt = wsProtocol(url.protocol);
const c = new W3CWebSocket(
`${wsProt}://${url.hostname}:${port}/ws/watch/${bucketName}?prefix=${prefix}&suffix=${suffix}`
`${wsProt}://${url.hostname}:${port}${baseUrl}ws/watch/${bucketName}?prefix=${prefix}&suffix=${suffix}`
);
let interval: any | null = null;

View File

@@ -19,13 +19,12 @@ package restapi
// list of all console environment constants
const (
// Constants for common configuration
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
ConsoleSubnetProxy = "CONSOLE_SUBNET_PROXY"
ConsoleMinIORegion = "CONSOLE_MINIO_REGION"
ConsoleHostname = "CONSOLE_HOSTNAME"
ConsolePort = "CONSOLE_PORT"
ConsoleTLSPort = "CONSOLE_TLS_PORT"
ConsoleSubnetLicense = "CONSOLE_SUBNET_LICENSE"
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
ConsoleSubnetProxy = "CONSOLE_SUBNET_PROXY"
ConsoleMinIORegion = "CONSOLE_MINIO_REGION"
ConsoleHostname = "CONSOLE_HOSTNAME"
ConsolePort = "CONSOLE_PORT"
ConsoleTLSPort = "CONSOLE_TLS_PORT"
// Constants for Secure middleware
ConsoleSecureAllowedHosts = "CONSOLE_SECURE_ALLOWED_HOSTS"