Repository: gorilla/websocket (go)

Issue:
[BUG] WebSocket server does not send 426 Upgrade Required
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

WebSocket server sends error 400 Bad Request when HTTP connection cannot be upgraded to WebSocket what is misleading - recently I had to exchange few emails with someone before I found that he was sending regular HTTP request instead of opening WS.

### Expected Behavior

Send error 426 Upgrade Required as specified in RFCs:

[RFC 9110 - HTTP Semantics](https://httpwg.org/specs/rfc9110.html#status.426):

> The 426 (Upgrade Required) status code indicates that the server refuses to perform the request using the current protocol but might be willing to do so after the client upgrades to a different protocol. The server MUST send an [Upgrade](https://httpwg.org/specs/rfc9110.html#field.upgrade) header field in a 426 response to indicate the required protocol(s) ([Section 7.8](https://httpwg.org/specs/rfc9110.html#field.upgrade)).

[RFC 6455 - The WebSocket Protocol](https://www.rfc-editor.org/rfc/rfc6455.html#section-4.2.2)

>        /version/
>           The |Sec-WebSocket-Version| header field in the client's
>           handshake includes the version of the WebSocket Protocol with
>           which the client is attempting to communicate.  If this
>           version does not match a version understood by the server, the
>           server MUST abort the WebSocket handshake described in this
>           section and instead send an appropriate HTTP error code (such
>           as 426 Upgrade Required) and a |Sec-WebSocket-Version| header
>           field indicating the version(s) the server is capable of
>           understanding.

### Steps To Reproduce

_No response_

### Anything else?

_No response_

Pull request:
Return 426 status on missing upgrade header
## What type of PR is this? (check all applicable)

- [ ] Refactor
- [ ] Feature
- [x] Bug Fix
- [ ] Optimization
- [ ] Documentation Update
- [ ] Go Version Update
- [ ] Dependency Update

## Description

Return 426 status on missing upgrade header.

## Related Tickets & Documents

- Closes #915

## Added/updated tests?

- [x] Yes
- [ ] No, and this is why: _please replace this line with details on why tests
      have not been included_
- [ ] I need help with writing tests

## Run verifications and test

- [ ] `make verify` is passing
- [ ] `make test` is passing


Patch:
diff --git a/server.go b/server.go
index ff7d03a..28f2013 100644
--- a/server.go
+++ b/server.go
@@ -130,7 +130,8 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
 	}
 
 	if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
-		return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
+		w.Header().Set("Upgrade", "websocket")
+		return u.returnError(w, r, http.StatusUpgradeRequired, badHandshake+"'websocket' token not found in 'Upgrade' header")
 	}
 
 	if r.Method != http.MethodGet {


Test patch:
diff --git a/client_server_test.go b/client_server_test.go
index 610fbe2..c5c08d7 100644
--- a/client_server_test.go
+++ b/client_server_test.go
@@ -497,6 +497,37 @@ func TestBadMethod(t *testing.T) {
 	}
 }
 
+func TestNoUpgrade(t *testing.T) {
+	t.Parallel()
+	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		ws, err := cstUpgrader.Upgrade(w, r, nil)
+		if err == nil {
+			t.Errorf("handshake succeeded, expect fail")
+			ws.Close()
+		}
+	}))
+	defer s.Close()
+
+	req, err := http.NewRequest(http.MethodGet, s.URL, strings.NewReader(""))
+	if err != nil {
+		t.Fatalf("NewRequest returned error %v", err)
+	}
+	req.Header.Set("Connection", "upgrade")
+	req.Header.Set("Sec-Websocket-Version", "13")
+
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		t.Fatalf("Do returned error %v", err)
+	}
+	resp.Body.Close()
+	if u := resp.Header.Get("Upgrade"); u != "websocket" {
+		t.Errorf("Uprade response header is %q, want %q", u, "websocket")
+	}
+	if resp.StatusCode != http.StatusUpgradeRequired {
+		t.Errorf("Status = %d, want %d", resp.StatusCode, http.StatusUpgradeRequired)
+	}
+}
+
 func TestDialExtraTokensInRespHeaders(t *testing.T) {
 	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		challengeKey := r.Header.Get("Sec-Websocket-Key")