mirror of https://github.com/halo-dev/halo
Ignore websocket protocol while proxying console (#2760)
#### What type of PR is this? /kind feature /area core #### What this PR does / why we need it: Ignore websocket protocol while proxying console. If we don't do this, console dev environment will crash with following error: ```bash VITE v3.2.4 ready in 1672 ms ➜ Local: http://localhost:3000/console/ 16:12:21 ➜ Network: http://172.23.176.1:3000/console/ 16:12:21 ➜ Network: http://172.18.96.1:3000/console/ 16:12:21 ➜ Network: http://192.168.31.106:3000/console/ 16:12:21 [vite-plugin-static-copy] Collected 8 items. 16:12:21 node:events:491 throw er; // Unhandled 'error' event ^ RangeError: Invalid WebSocket frame: RSV1 must be clear at Receiver$1.getInfo (file:///C:/Users/johnn/workspaces/halo-dev/console/node_modules/.pnpm/vite@3.2.4_ajklay5k626t46b6fyghkbup3i/node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:54186:14) at Receiver$1.startLoop (file:///C:/Users/johnn/workspaces/halo-dev/console/node_modules/.pnpm/vite@3.2.4_ajklay5k626t46b6fyghkbup3i/node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:54133:22) at Receiver$1._write (file:///C:/Users/johnn/workspaces/halo-dev/console/node_modules/.pnpm/vite@3.2.4_ajklay5k626t46b6fyghkbup3i/node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:54080:10) at writeOrBuffer (node:internal/streams/writable:392:12) at _write (node:internal/streams/writable:333:10) at Writable.write (node:internal/streams/writable:337:10) at Socket.socketOnData (file:///C:/Users/johnn/workspaces/halo-dev/console/node_modules/.pnpm/vite@3.2.4_ajklay5k626t46b6fyghkbup3i/node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:56826:37) at Socket.emit (node:events:513:28) at addChunk (node:internal/streams/readable:324:12) at readableAddChunk (node:internal/streams/readable:297:9) at Readable.push (node:internal/streams/readable:234:10) at TCP.onStreamRead (node:internal/stream_base_commons:190:23) Emitted 'error' event on WebSocket$1 instance at: at Receiver$1.receiverOnError (file:///C:/Users/johnn/workspaces/halo-dev/console/node_modules/.pnpm/vite@3.2.4_ajklay5k626t46b6fyghkbup3i/node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:56712:13) at Receiver$1.emit (node:events:513:28) at emitErrorNT (node:internal/streams/destroy:151:8) at emitErrorCloseNT (node:internal/streams/destroy:116:3) at process.processTicksAndRejections (node:internal/process/task_queues:82:21) { code: 'WS_ERR_UNEXPECTED_RSV_1', [Symbol(status-code)]: 1002 } Node.js v18.12.1 ELIFECYCLE Command failed with exit code 1. ``` #### Special notes for your reviewer: Steps to test: 1. Edit your application.yaml with console proxy configuration 2. Start Halo 3. Use non-Chromelike browser to request <http://localhost:8090>. 4. See the result #### Does this PR introduce a user-facing change? ```release-note None ```pull/2741/head^2
parent
f96ef7f1b3
commit
edfc9ac1e1
|
@ -5,6 +5,7 @@ import org.springframework.http.HttpMethod;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.util.matcher.MediaTypeServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
|
@ -31,6 +32,8 @@ public class ConsoleProxyFilter implements WebFilter {
|
|||
var consoleMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/console/**");
|
||||
consoleMatcher = new AndServerWebExchangeMatcher(consoleMatcher,
|
||||
new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML));
|
||||
consoleMatcher = new AndServerWebExchangeMatcher(consoleMatcher,
|
||||
new NegatedServerWebExchangeMatcher(new WebSocketServerWebExchangeMatcher()));
|
||||
this.consoleMatcher = consoleMatcher;
|
||||
this.webClient = WebClient.create(proxyProperties.getEndpoint().toString());
|
||||
log.info("Initialized ConsoleProxyFilter to proxy console");
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package run.halo.app.console;
|
||||
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class WebSocketServerWebExchangeMatcher implements ServerWebExchangeMatcher {
|
||||
@Override
|
||||
public Mono<MatchResult> matches(ServerWebExchange exchange) {
|
||||
var headers = exchange.getRequest().getHeaders();
|
||||
if (!headers.getConnection().contains("Upgrade")) {
|
||||
return MatchResult.notMatch();
|
||||
}
|
||||
var upgrade = headers.getUpgrade();
|
||||
if (!"websocket".equalsIgnoreCase(upgrade)) {
|
||||
return MatchResult.notMatch();
|
||||
}
|
||||
return MatchResult.match();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package run.halo.app.console;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
class WebSocketServerWebExchangeMatcherTest {
|
||||
|
||||
@Test
|
||||
void shouldMatchIfWebSocketProtocol() {
|
||||
var httpRequest = MockServerHttpRequest.get("")
|
||||
.header(HttpHeaders.CONNECTION, HttpHeaders.UPGRADE)
|
||||
.header(HttpHeaders.UPGRADE, "websocket")
|
||||
.build();
|
||||
var wsExchange = MockServerWebExchange.from(httpRequest);
|
||||
var wsMatcher = new WebSocketServerWebExchangeMatcher();
|
||||
StepVerifier.create(wsMatcher.matches(wsExchange))
|
||||
.consumeNextWith(result -> assertTrue(result.isMatch()))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotMatchIfNotWebSocketProtocol() {
|
||||
var httpRequest = MockServerHttpRequest.get("")
|
||||
.header(HttpHeaders.CONNECTION, HttpHeaders.UPGRADE)
|
||||
.header(HttpHeaders.UPGRADE, "not-a-websocket")
|
||||
.build();
|
||||
var wsExchange = MockServerWebExchange.from(httpRequest);
|
||||
var wsMatcher = new WebSocketServerWebExchangeMatcher();
|
||||
StepVerifier.create(wsMatcher.matches(wsExchange))
|
||||
.consumeNextWith(result -> assertFalse(result.isMatch()))
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue