@@ -118,12 +118,26 @@ public boolean isWebSocketSupported() {
118118 return WebSockets .isSupported ();
119119 }
120120
121+ /**
122+ * Unlike {@link HttpResponses#errorWithoutStack} this sends the message in a header rather than the body.
123+ * (Currently the WebSocket CLI is unable to process the body in an error message.)
124+ */
125+ private static HttpResponse statusWithExplanation (int code , String errorMessage ) {
126+ return new HttpResponse () {
127+ @ Override
128+ public void generateResponse (StaplerRequest2 req , StaplerResponse2 rsp , Object node ) {
129+ rsp .setStatus (code );
130+ rsp .setHeader ("X-CLI-Error" , errorMessage );
131+ }
132+ };
133+ }
134+
121135 /**
122136 * WebSocket endpoint.
123137 */
124138 public HttpResponse doWs (StaplerRequest2 req ) {
125139 if (!WebSockets .isSupported ()) {
126- return HttpResponses . notFound ( );
140+ return statusWithExplanation ( HttpServletResponse . SC_NOT_FOUND , "WebSocket is not supported in this servlet container (try the built-in Jetty instead)" );
127141 }
128142 if (ALLOW_WEBSOCKET == null ) {
129143 final String actualOrigin = req .getHeader ("Origin" );
@@ -141,10 +155,10 @@ public HttpResponse doWs(StaplerRequest2 req) {
141155
142156 if (actualOrigin == null || !actualOrigin .equals (expectedOrigin )) {
143157 LOGGER .log (Level .FINE , () -> "Rejecting origin: " + actualOrigin + "; expected was from request: " + expectedOrigin );
144- return HttpResponses . forbidden ( );
158+ return statusWithExplanation ( HttpServletResponse . SC_FORBIDDEN , "Unexpected request origin (check your reverse proxy settings)" );
145159 }
146160 } else if (!ALLOW_WEBSOCKET ) {
147- return HttpResponses . forbidden ( );
161+ return statusWithExplanation ( HttpServletResponse . SC_FORBIDDEN , "WebSocket support for CLI disabled for this controller" );
148162 }
149163 Authentication authentication = Jenkins .getAuthentication2 ();
150164 return WebSockets .upgrade (new WebSocketSession () {
0 commit comments