mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
fix: disable websocket compression for startup logs in Safari (#8087)
This commit is contained in:
committed by
GitHub
parent
c3781d95b4
commit
b8ba287128
6
coderd/apidoc/docs.go
generated
6
coderd/apidoc/docs.go
generated
@ -4599,6 +4599,12 @@ const docTemplate = `{
|
|||||||
"description": "Follow log stream",
|
"description": "Follow log stream",
|
||||||
"name": "follow",
|
"name": "follow",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Disable compression for WebSocket connection",
|
||||||
|
"name": "no_compression",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
6
coderd/apidoc/swagger.json
generated
6
coderd/apidoc/swagger.json
generated
@ -4049,6 +4049,12 @@
|
|||||||
"description": "Follow log stream",
|
"description": "Follow log stream",
|
||||||
"name": "follow",
|
"name": "follow",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Disable compression for WebSocket connection",
|
||||||
|
"name": "no_compression",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
@ -405,6 +405,7 @@ func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.R
|
|||||||
// @Param before query int false "Before log id"
|
// @Param before query int false "Before log id"
|
||||||
// @Param after query int false "After log id"
|
// @Param after query int false "After log id"
|
||||||
// @Param follow query bool false "Follow log stream"
|
// @Param follow query bool false "Follow log stream"
|
||||||
|
// @Param no_compression query bool false "Disable compression for WebSocket connection"
|
||||||
// @Success 200 {array} codersdk.WorkspaceAgentStartupLog
|
// @Success 200 {array} codersdk.WorkspaceAgentStartupLog
|
||||||
// @Router /workspaceagents/{workspaceagent}/startup-logs [get]
|
// @Router /workspaceagents/{workspaceagent}/startup-logs [get]
|
||||||
func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Request) {
|
func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Request) {
|
||||||
@ -415,6 +416,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||||||
logger = api.Logger.With(slog.F("workspace_agent_id", workspaceAgent.ID))
|
logger = api.Logger.With(slog.F("workspace_agent_id", workspaceAgent.ID))
|
||||||
follow = r.URL.Query().Has("follow")
|
follow = r.URL.Query().Has("follow")
|
||||||
afterRaw = r.URL.Query().Get("after")
|
afterRaw = r.URL.Query().Get("after")
|
||||||
|
noCompression = r.URL.Query().Has("no_compression")
|
||||||
)
|
)
|
||||||
|
|
||||||
var after int64
|
var after int64
|
||||||
@ -460,7 +462,21 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||||||
api.WebsocketWaitGroup.Add(1)
|
api.WebsocketWaitGroup.Add(1)
|
||||||
api.WebsocketWaitMutex.Unlock()
|
api.WebsocketWaitMutex.Unlock()
|
||||||
defer api.WebsocketWaitGroup.Done()
|
defer api.WebsocketWaitGroup.Done()
|
||||||
conn, err := websocket.Accept(rw, r, nil)
|
|
||||||
|
opts := &websocket.AcceptOptions{}
|
||||||
|
|
||||||
|
// Allow client to request no compression. This is useful for buggy
|
||||||
|
// clients or if there's a client/server incompatibility. This is
|
||||||
|
// needed with e.g. nhooyr/websocket and Safari (confirmed in 16.5).
|
||||||
|
//
|
||||||
|
// See:
|
||||||
|
// * https://github.com/nhooyr/websocket/issues/218
|
||||||
|
// * https://github.com/gobwas/ws/issues/169
|
||||||
|
if noCompression {
|
||||||
|
opts.CompressionMode = websocket.CompressionDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := websocket.Accept(rw, r, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||||
Message: "Failed to accept websocket.",
|
Message: "Failed to accept websocket.",
|
||||||
|
@ -689,12 +689,13 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/sta
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
| Name | In | Type | Required | Description |
|
| Name | In | Type | Required | Description |
|
||||||
| ---------------- | ----- | ------------ | -------- | ------------------ |
|
| ---------------- | ----- | ------------ | -------- | -------------------------------------------- |
|
||||||
| `workspaceagent` | path | string(uuid) | true | Workspace agent ID |
|
| `workspaceagent` | path | string(uuid) | true | Workspace agent ID |
|
||||||
| `before` | query | integer | false | Before log id |
|
| `before` | query | integer | false | Before log id |
|
||||||
| `after` | query | integer | false | After log id |
|
| `after` | query | integer | false | After log id |
|
||||||
| `follow` | query | boolean | false | Follow log stream |
|
| `follow` | query | boolean | false | Follow log stream |
|
||||||
|
| `no_compression` | query | boolean | false | Disable compression for WebSocket connection |
|
||||||
|
|
||||||
### Example responses
|
### Example responses
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import * as Types from "./types"
|
|||||||
import { DeploymentConfig } from "./types"
|
import { DeploymentConfig } from "./types"
|
||||||
import * as TypesGen from "./typesGenerated"
|
import * as TypesGen from "./typesGenerated"
|
||||||
import { delay } from "utils/delay"
|
import { delay } from "utils/delay"
|
||||||
|
import userAgentParser from "ua-parser-js"
|
||||||
|
|
||||||
// Adds 304 for the default axios validateStatus function
|
// Adds 304 for the default axios validateStatus function
|
||||||
// https://github.com/axios/axios#handling-errors Check status here
|
// https://github.com/axios/axios#handling-errors Check status here
|
||||||
@ -1231,9 +1232,19 @@ export const watchStartupLogs = (
|
|||||||
agentId: string,
|
agentId: string,
|
||||||
{ after, onMessage, onDone, onError }: WatchStartupLogsOptions,
|
{ after, onMessage, onDone, onError }: WatchStartupLogsOptions,
|
||||||
) => {
|
) => {
|
||||||
|
// WebSocket compression in Safari (confirmed in 16.5) is broken when
|
||||||
|
// the server sends large messages. The following error is seen:
|
||||||
|
//
|
||||||
|
// WebSocket connection to 'wss://.../startup-logs?follow&after=0' failed: The operation couldn’t be completed. Protocol error
|
||||||
|
//
|
||||||
|
const noCompression =
|
||||||
|
userAgentParser(navigator.userAgent).browser.name === "Safari"
|
||||||
|
? "&no_compression"
|
||||||
|
: ""
|
||||||
|
|
||||||
const proto = location.protocol === "https:" ? "wss:" : "ws:"
|
const proto = location.protocol === "https:" ? "wss:" : "ws:"
|
||||||
const socket = new WebSocket(
|
const socket = new WebSocket(
|
||||||
`${proto}//${location.host}/api/v2/workspaceagents/${agentId}/startup-logs?follow&after=${after}`,
|
`${proto}//${location.host}/api/v2/workspaceagents/${agentId}/startup-logs?follow&after=${after}${noCompression}`,
|
||||||
)
|
)
|
||||||
socket.binaryType = "blob"
|
socket.binaryType = "blob"
|
||||||
socket.addEventListener("message", (event) => {
|
socket.addEventListener("message", (event) => {
|
||||||
|
Reference in New Issue
Block a user