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:
@@ -29,6 +29,9 @@ import (
|
|||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
url2 "net/url"
|
url2 "net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
|
||||||
v2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
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()
|
defer loginResp.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if tenantCookie == nil {
|
if tenantCookie == nil {
|
||||||
log.Println(errors.New("couldn't login to tenant and get cookie"))
|
log.Println(errors.New("couldn't login to tenant and get cookie"))
|
||||||
responseWriter.WriteHeader(500)
|
responseWriter.WriteHeader(403)
|
||||||
return
|
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)
|
targetURL, err := url2.Parse(tenantURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -206,7 +213,16 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
proxyCookieJar, _ := cookiejar.New(nil)
|
proxyCookieJar, _ := cookiejar.New(nil)
|
||||||
proxyCookieJar.SetCookies(targetURL, []*http.Cookie{proxiedCookie})
|
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{
|
tr := &http.Transport{
|
||||||
// FIXME: use restapi.GetConsoleHTTPClient()
|
// FIXME: use restapi.GetConsoleHTTPClient()
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
@@ -259,5 +275,75 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
|
|||||||
responseWriter.WriteHeader(resp.StatusCode)
|
responseWriter.WriteHeader(resp.StatusCode)
|
||||||
|
|
||||||
io.Copy(responseWriter, resp.Body)
|
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:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,9 +188,13 @@ const Heal = ({ classes, distributedSetup }: IHeal) => {
|
|||||||
const isDev = process.env.NODE_ENV === "development";
|
const isDev = process.env.NODE_ENV === "development";
|
||||||
const port = isDev ? "9090" : url.port;
|
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 wsProt = wsProtocol(url.protocol);
|
||||||
const c = new W3CWebSocket(
|
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) {
|
if (c !== null) {
|
||||||
|
|||||||
@@ -173,8 +173,12 @@ const HealthInfo = ({
|
|||||||
|
|
||||||
const wsProt = wsProtocol(url.protocol);
|
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(
|
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;
|
let interval: any | null = null;
|
||||||
|
|||||||
@@ -158,11 +158,14 @@ const ErrorLogs = ({
|
|||||||
const port = isDev ? "9090" : url.port;
|
const port = isDev ? "9090" : url.port;
|
||||||
|
|
||||||
const wsProt = wsProtocol(url.protocol);
|
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(
|
c = new W3CWebSocket(
|
||||||
`${wsProt}://${
|
`${wsProt}://${
|
||||||
url.hostname
|
url.hostname
|
||||||
}:${port}/ws/console/?logType=${logType}&node=${
|
}:${port}${baseUrl}ws/console/?logType=${logType}&node=${
|
||||||
selectedNode === "Select node" ? "" : selectedNode
|
selectedNode === "Select node" ? "" : selectedNode
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -109,9 +109,13 @@ const Speedtest = ({ classes, distributedSetup }: ISpeedtest) => {
|
|||||||
const isDev = process.env.NODE_ENV === "development";
|
const isDev = process.env.NODE_ENV === "development";
|
||||||
const port = isDev ? "9090" : url.port;
|
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 wsProt = wsProtocol(url.protocol);
|
||||||
const c = new W3CWebSocket(
|
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();
|
const baseDate = moment();
|
||||||
|
|||||||
@@ -166,12 +166,15 @@ const Trace = ({
|
|||||||
if (all) {
|
if (all) {
|
||||||
calls = "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);
|
const wsProt = wsProtocol(url.protocol);
|
||||||
c = new W3CWebSocket(
|
c = new W3CWebSocket(
|
||||||
`${wsProt}://${
|
`${wsProt}://${
|
||||||
url.hostname
|
url.hostname
|
||||||
}:${port}/ws/trace?calls=${calls}&threshold=${threshold}&onlyErrors=${
|
}:${port}${baseUrl}ws/trace?calls=${calls}&threshold=${threshold}&onlyErrors=${
|
||||||
errors ? "yes" : "no"
|
errors ? "yes" : "no"
|
||||||
}&statusCode=${statusCode}&method=${method}&funcname=${func}&path=${path}`
|
}&statusCode=${statusCode}&method=${method}&funcname=${func}&path=${path}`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -128,9 +128,13 @@ const Watch = ({
|
|||||||
const isDev = process.env.NODE_ENV === "development";
|
const isDev = process.env.NODE_ENV === "development";
|
||||||
const port = isDev ? "9090" : url.port;
|
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 wsProt = wsProtocol(url.protocol);
|
||||||
const c = new W3CWebSocket(
|
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;
|
let interval: any | null = null;
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ package restapi
|
|||||||
// list of all console environment constants
|
// list of all console environment constants
|
||||||
const (
|
const (
|
||||||
// Constants for common configuration
|
// Constants for common configuration
|
||||||
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
|
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
|
||||||
ConsoleSubnetProxy = "CONSOLE_SUBNET_PROXY"
|
ConsoleSubnetProxy = "CONSOLE_SUBNET_PROXY"
|
||||||
ConsoleMinIORegion = "CONSOLE_MINIO_REGION"
|
ConsoleMinIORegion = "CONSOLE_MINIO_REGION"
|
||||||
ConsoleHostname = "CONSOLE_HOSTNAME"
|
ConsoleHostname = "CONSOLE_HOSTNAME"
|
||||||
ConsolePort = "CONSOLE_PORT"
|
ConsolePort = "CONSOLE_PORT"
|
||||||
ConsoleTLSPort = "CONSOLE_TLS_PORT"
|
ConsoleTLSPort = "CONSOLE_TLS_PORT"
|
||||||
ConsoleSubnetLicense = "CONSOLE_SUBNET_LICENSE"
|
|
||||||
|
|
||||||
// Constants for Secure middleware
|
// Constants for Secure middleware
|
||||||
ConsoleSecureAllowedHosts = "CONSOLE_SECURE_ALLOWED_HOSTS"
|
ConsoleSecureAllowedHosts = "CONSOLE_SECURE_ALLOWED_HOSTS"
|
||||||
|
|||||||
Reference in New Issue
Block a user