dotnet / dotnet/aspnetcore

Inconsistency between IIS and Kestrel for websocket connect operation with duplicate 'Upgrade: websocket, websocket' header

Open
#50,364 2 comments 2 reactions 2 assignees Claimed by @DeagleGross View on GitHub
area-networking bug feature-iis feature-websockets help wanted
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

Accidentally I've sent a websocket connect request with duplicate `websocket` header like here:
![image](https://github.com/dotnet/aspnetcore/assets/31598696/9e80f883-2089-4b86-b883-f9752c5e6a8d)

It worked on a Kestrel based app: websocket connection was established, and I could send \ receive messages and gracefully terminate a connection.

- It failed on IIS hosted app: after calling `context.WebSockets.AcceptWebSocketAsync()` `websocket.State` is `Open`
![image](https://github.com/dotnet/aspnetcore/assets/31598696/bd81fdc7-ff6a-4248-aa9c-05f19dff8424)
- but client immediately disconnected:
```
Could not connect to ws://localhost:21156
12:00:24
Error: Unexpected server response: 101
Handshake Details
Request URL: http://localhost:21156/
Request Method: GET
Status Code: 101 Switching Protocols
Request Headers
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: gCyKijP/CPAUtW3Cjyf8gA==
Connection: Upgrade
Upgrade: websocket, websocket
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Host: localhost:21156
Response Headers
Transfer-Encoding: chunked
Upgrade: websocket
Server: Microsoft-IIS/10.0
Sec-WebSocket-Accept: fiwTshwZAtNTY2V2+ynOPK4kFNk=
X-Powered-By: ASP.NET
Date: Mon, 28 Aug 2023 10:00:24 GMT
```

### Expected Behavior

As per my understanding, either Kestrel implementation should fail as well, or IIS should handle duplicate `Upgrade: websocket, websocket` header. Let me know, if I am doing something wrong.

### Steps To Reproduce

repro is as simple as creating an ASP.NET core project and using such setup in `Program.cs`:
```
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
await next(context);
}
});

app.Run();
```

and make a websocket connect call with `Upgrade: websocket, websocket` header.

if you need a repro in a repository:
- [run WebApp](https://github.com/DeagleGross/repro-1/tree/main/WebApp/WebApp)
- [run client](https://github.com/DeagleGross/repro-1/tree/main/WebApp/Client)

### Exceptions (if any)

```
Unhandled exception. System.Net.WebSockets.WebSocketException (203): The server's response was missing the required header 'Connection'.
at System.Net.WebSockets.WebSocketHandle.ValidateHeader(HttpHeaders headers, String name, String expectedValue)
at System.Net.WebSockets.WebSocketHandle.ConnectAsync(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
at System.Net.WebSockets.ClientWebSocket.ConnectAsyncCore(Uri uri, CancellationToken cancellationToken)
at Program.$(String[] args) in C:\code\personal\repro-1\WebApp\Client\Program.cs:line 10
at Program.(String[] args)
```

### .NET Version

net6.0 (local 6.0.316)

### Anything else?

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.