workspaceAgentPTY spawns a PTY and pipes it over a WebSocket. This is used for the web terminal. @Summary Open PTY to workspace agent @ID open-pty-to-workspace-agent @Security CoderSessionToken @Tags Agents @Param workspaceagent path string true "Workspace agent ID" format(uuid) @Success 101 @Route
(rw http.ResponseWriter, r *http.Request)
| 703 | // @Success 101 |
| 704 | // @Router /api/v2/workspaceagents/{workspaceagent}/pty [get] |
| 705 | func (s *Server) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { |
| 706 | ctx, cancel := context.WithCancel(r.Context()) |
| 707 | defer cancel() |
| 708 | |
| 709 | s.websocketWaitMutex.Lock() |
| 710 | s.websocketWaitGroup.Add(1) |
| 711 | s.websocketWaitMutex.Unlock() |
| 712 | defer s.websocketWaitGroup.Done() |
| 713 | |
| 714 | appToken, ok := ResolveRequest(rw, r, ResolveRequestOptions{ |
| 715 | Logger: s.Logger, |
| 716 | Cookies: s.cookies, |
| 717 | CookieCfg: s.CookiesConfig, |
| 718 | SignedTokenProvider: s.SignedTokenProvider, |
| 719 | DashboardURL: s.DashboardURL, |
| 720 | PathAppBaseURL: s.AccessURL, |
| 721 | AppHostname: s.Hostname, |
| 722 | AppRequest: Request{ |
| 723 | AccessMethod: AccessMethodTerminal, |
| 724 | BasePath: r.URL.Path, |
| 725 | AgentNameOrID: chi.URLParam(r, "workspaceagent"), |
| 726 | }, |
| 727 | AppPath: "", |
| 728 | AppQuery: "", |
| 729 | }) |
| 730 | if !ok { |
| 731 | return |
| 732 | } |
| 733 | log := s.Logger.With(slog.F("agent_id", appToken.AgentID)) |
| 734 | log.Debug(ctx, "resolved PTY request") |
| 735 | |
| 736 | values := r.URL.Query() |
| 737 | parser := httpapi.NewQueryParamParser() |
| 738 | reconnect := parser.RequiredNotEmpty("reconnect").UUID(values, uuid.New(), "reconnect") |
| 739 | height := parser.UInt(values, 80, "height") |
| 740 | width := parser.UInt(values, 80, "width") |
| 741 | container := parser.String(values, "", "container") |
| 742 | containerUser := parser.String(values, "", "container_user") |
| 743 | backendType := parser.String(values, "", "backend_type") |
| 744 | if len(parser.Errors) > 0 { |
| 745 | httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ |
| 746 | Message: "Invalid query parameters.", |
| 747 | Validations: parser.Errors, |
| 748 | }) |
| 749 | return |
| 750 | } |
| 751 | |
| 752 | conn, err := websocket.Accept(rw, r, &websocket.AcceptOptions{ |
| 753 | CompressionMode: websocket.CompressionDisabled, |
| 754 | // Always allow websockets from the primary dashboard URL. |
| 755 | // Terminals are opened there and connect to the proxy. |
| 756 | OriginPatterns: []string{ |
| 757 | s.DashboardURL.Host, |
| 758 | s.AccessURL.Host, |
| 759 | }, |
| 760 | }) |
| 761 | if err != nil { |
| 762 | httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ |
nothing calls this directly
no test coverage detected