Skip to content

Commit b36aed8

Browse files
authored
Adds support to proxy WS requests on Hop (#1857)
Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
1 parent 991cc09 commit b36aed8

File tree

8 files changed

+121
-14
lines changed

8 files changed

+121
-14
lines changed

operatorapi/proxy.go

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import (
2929
"net/http/cookiejar"
3030
url2 "net/url"
3131
"strings"
32+
"time"
33+
34+
"github.com/gorilla/websocket"
3235

3336
v2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
3437

@@ -181,11 +184,15 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
181184
}
182185
defer loginResp.Body.Close()
183186
}
187+
184188
if tenantCookie == nil {
185189
log.Println(errors.New("couldn't login to tenant and get cookie"))
186-
responseWriter.WriteHeader(500)
190+
responseWriter.WriteHeader(403)
187191
return
188192
}
193+
// at this point we have a valid cookie ready to either route HTTP or WS
194+
// now we need to know if we are doing an /api/ call (http) or /ws/ call (ws)
195+
callType := urlParts[5]
189196

190197
targetURL, err := url2.Parse(tenantURL)
191198
if err != nil {
@@ -206,7 +213,16 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
206213

207214
proxyCookieJar, _ := cookiejar.New(nil)
208215
proxyCookieJar.SetCookies(targetURL, []*http.Cookie{proxiedCookie})
216+
switch callType {
217+
case "ws":
218+
handleWSRequest(responseWriter, req, proxyCookieJar, targetURL, tenantSchema)
219+
default:
220+
handleHTTPRequest(responseWriter, req, proxyCookieJar, tenantBase, targetURL)
221+
}
209222

223+
}
224+
225+
func handleHTTPRequest(responseWriter http.ResponseWriter, req *http.Request, proxyCookieJar *cookiejar.Jar, tenantBase string, targetURL *url2.URL) {
210226
tr := &http.Transport{
211227
// FIXME: use restapi.GetConsoleHTTPClient()
212228
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
@@ -259,5 +275,75 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
259275
responseWriter.WriteHeader(resp.StatusCode)
260276

261277
io.Copy(responseWriter, resp.Body)
278+
}
262279

280+
var upgrader = websocket.Upgrader{
281+
ReadBufferSize: 0,
282+
WriteBufferSize: 1024,
283+
}
284+
285+
func handleWSRequest(responseWriter http.ResponseWriter, req *http.Request, proxyCookieJar *cookiejar.Jar, targetURL *url2.URL, schema string) {
286+
dialer := &websocket.Dialer{
287+
Proxy: http.ProxyFromEnvironment,
288+
HandshakeTimeout: 45 * time.Second,
289+
Jar: proxyCookieJar,
290+
}
291+
292+
upgrader.CheckOrigin = func(r *http.Request) bool {
293+
return true
294+
}
295+
296+
c, err := upgrader.Upgrade(responseWriter, req, nil)
297+
if err != nil {
298+
log.Print("error upgrade connection:", err)
299+
return
300+
}
301+
defer c.Close()
302+
if schema == "http" {
303+
targetURL.Scheme = "ws"
304+
} else {
305+
targetURL.Scheme = "wss"
306+
}
307+
308+
// establish a websocket to the tenant
309+
tenantConn, _, err := dialer.Dial(targetURL.String(), nil)
310+
if err != nil {
311+
log.Println("dial:", err)
312+
return
313+
}
314+
defer tenantConn.Close()
315+
316+
doneTenant := make(chan struct{})
317+
done := make(chan struct{})
318+
319+
// start read pump from tenant connection
320+
go func() {
321+
defer close(doneTenant)
322+
for {
323+
msgType, message, err := tenantConn.ReadMessage()
324+
if err != nil {
325+
log.Println("error read from tenant:", err)
326+
return
327+
}
328+
c.WriteMessage(msgType, message)
329+
}
330+
}()
331+
332+
// start read pump from tenant connection
333+
go func() {
334+
defer close(done)
335+
for {
336+
msgType, message, err := c.ReadMessage()
337+
if err != nil {
338+
log.Println("error read from client:", err)
339+
return
340+
}
341+
tenantConn.WriteMessage(msgType, message)
342+
}
343+
}()
344+
345+
select {
346+
case <-done:
347+
case <-doneTenant:
348+
}
263349
}

portal-ui/src/screens/Console/Heal/Heal.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,13 @@ const Heal = ({ classes, distributedSetup }: IHeal) => {
188188
const isDev = process.env.NODE_ENV === "development";
189189
const port = isDev ? "9090" : url.port;
190190

191+
// check if we are using base path, if not this always is `/`
192+
const baseLocation = new URL(document.baseURI);
193+
const baseUrl = baseLocation.pathname;
194+
191195
const wsProt = wsProtocol(url.protocol);
192196
const c = new W3CWebSocket(
193-
`${wsProt}://${url.hostname}:${port}/ws/heal/${bucketName}?prefix=${prefix}&recursive=${recursive}&force-start=${forceStart}&force-stop=${forceStop}`
197+
`${wsProt}://${url.hostname}:${port}${baseUrl}ws/heal/${bucketName}?prefix=${prefix}&recursive=${recursive}&force-start=${forceStart}&force-stop=${forceStop}`
194198
);
195199

196200
if (c !== null) {

portal-ui/src/screens/Console/HealthInfo/HealthInfo.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,12 @@ const HealthInfo = ({
173173

174174
const wsProt = wsProtocol(url.protocol);
175175

176+
// check if we are using base path, if not this always is `/`
177+
const baseLocation = new URL(document.baseURI);
178+
const baseUrl = baseLocation.pathname;
179+
176180
const c = new W3CWebSocket(
177-
`${wsProt}://${url.hostname}:${port}/ws/health-info?deadline=1h`
181+
`${wsProt}://${url.hostname}:${port}${baseUrl}ws/health-info?deadline=1h`
178182
);
179183

180184
let interval: any | null = null;

portal-ui/src/screens/Console/Logs/ErrorLogs/ErrorLogs.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,14 @@ const ErrorLogs = ({
158158
const port = isDev ? "9090" : url.port;
159159

160160
const wsProt = wsProtocol(url.protocol);
161+
// check if we are using base path, if not this always is `/`
162+
const baseLocation = new URL(document.baseURI);
163+
const baseUrl = baseLocation.pathname;
161164

162165
c = new W3CWebSocket(
163166
`${wsProt}://${
164167
url.hostname
165-
}:${port}/ws/console/?logType=${logType}&node=${
168+
}:${port}${baseUrl}ws/console/?logType=${logType}&node=${
166169
selectedNode === "Select node" ? "" : selectedNode
167170
}`
168171
);

portal-ui/src/screens/Console/Speedtest/Speedtest.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,13 @@ const Speedtest = ({ classes, distributedSetup }: ISpeedtest) => {
109109
const isDev = process.env.NODE_ENV === "development";
110110
const port = isDev ? "9090" : url.port;
111111

112+
// check if we are using base path, if not this always is `/`
113+
const baseLocation = new URL(document.baseURI);
114+
const baseUrl = baseLocation.pathname;
115+
112116
const wsProt = wsProtocol(url.protocol);
113117
const c = new W3CWebSocket(
114-
`${wsProt}://${url.hostname}:${port}/ws/speedtest?&size=${size}${sizeUnit}`
118+
`${wsProt}://${url.hostname}:${port}${baseUrl}ws/speedtest?&size=${size}${sizeUnit}`
115119
);
116120

117121
const baseDate = moment();

portal-ui/src/screens/Console/Trace/Trace.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,15 @@ const Trace = ({
166166
if (all) {
167167
calls = "all";
168168
}
169+
// check if we are using base path, if not this always is `/`
170+
const baseLocation = new URL(document.baseURI);
171+
const baseUrl = baseLocation.pathname;
169172

170173
const wsProt = wsProtocol(url.protocol);
171174
c = new W3CWebSocket(
172175
`${wsProt}://${
173176
url.hostname
174-
}:${port}/ws/trace?calls=${calls}&threshold=${threshold}&onlyErrors=${
177+
}:${port}${baseUrl}ws/trace?calls=${calls}&threshold=${threshold}&onlyErrors=${
175178
errors ? "yes" : "no"
176179
}&statusCode=${statusCode}&method=${method}&funcname=${func}&path=${path}`
177180
);

portal-ui/src/screens/Console/Watch/Watch.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,13 @@ const Watch = ({
128128
const isDev = process.env.NODE_ENV === "development";
129129
const port = isDev ? "9090" : url.port;
130130

131+
// check if we are using base path, if not this always is `/`
132+
const baseLocation = new URL(document.baseURI);
133+
const baseUrl = baseLocation.pathname;
134+
131135
const wsProt = wsProtocol(url.protocol);
132136
const c = new W3CWebSocket(
133-
`${wsProt}://${url.hostname}:${port}/ws/watch/${bucketName}?prefix=${prefix}&suffix=${suffix}`
137+
`${wsProt}://${url.hostname}:${port}${baseUrl}ws/watch/${bucketName}?prefix=${prefix}&suffix=${suffix}`
134138
);
135139

136140
let interval: any | null = null;

restapi/consts.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@ package restapi
1919
// list of all console environment constants
2020
const (
2121
// Constants for common configuration
22-
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
23-
ConsoleSubnetProxy = "CONSOLE_SUBNET_PROXY"
24-
ConsoleMinIORegion = "CONSOLE_MINIO_REGION"
25-
ConsoleHostname = "CONSOLE_HOSTNAME"
26-
ConsolePort = "CONSOLE_PORT"
27-
ConsoleTLSPort = "CONSOLE_TLS_PORT"
28-
ConsoleSubnetLicense = "CONSOLE_SUBNET_LICENSE"
22+
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
23+
ConsoleSubnetProxy = "CONSOLE_SUBNET_PROXY"
24+
ConsoleMinIORegion = "CONSOLE_MINIO_REGION"
25+
ConsoleHostname = "CONSOLE_HOSTNAME"
26+
ConsolePort = "CONSOLE_PORT"
27+
ConsoleTLSPort = "CONSOLE_TLS_PORT"
2928

3029
// Constants for Secure middleware
3130
ConsoleSecureAllowedHosts = "CONSOLE_SECURE_ALLOWED_HOSTS"

0 commit comments

Comments
 (0)