Repository: apollographql/apollo-kotlin (kotlin)

Issue:
Sub-protocol not included when opening websocket [4.0.0-beta6]
### Version

4.0.0-beta6

### Summary

I'm trying to migrate our project to `4.0.0-beta6`.

I've replaced:
```
import com.apollographql.apollo3.network.ws.GraphQLWsProtocol
import com.apollographql.apollo3.network.ws.WebSocketNetworkTransport

.subscriptionNetworkTransport(
                WebSocketNetworkTransport.Builder()
                    .serverUrl(BASE_URL)
                    .protocol(GraphQLWsProtocol.Factory())
                    .build()
            )
```

With:
```
import com.apollographql.apollo3.network.websocket.GraphQLWsProtocol
import com.apollographql.apollo3.network.websocket.WebSocketNetworkTransport

.subscriptionNetworkTransport(
                WebSocketNetworkTransport.Builder()
                    .serverUrl(BASE_URL)
                    .wsProtocol(GraphQLWsProtocol())
                    .build()
            )
```

But whenever our app attempts to open a websocket now, it does so without the including any sub protocol:
<img width="1033" alt="Screenshot 2024-06-05 at 13 14 00" src="https://github.com/apollographql/apollo-kotlin/assets/67371753/4c8caab8-8cb6-419c-b7f1-88ca06ab69d7">

Pull request:
Properly send `Sec-WebSocket-Protocol`, fixes #5946

Patch:
diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/network/websocket/internal/SubscribableWebSocket.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/network/websocket/internal/SubscribableWebSocket.kt
index 6a8702d3c9f..308b3cbd565 100644
--- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/network/websocket/internal/SubscribableWebSocket.kt
+++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/network/websocket/internal/SubscribableWebSocket.kt
@@ -65,7 +65,15 @@ internal class SubscribableWebSocket(
   private var pending = mutableListOf<ApolloRequest<*>>()
   private var _lastActiveMillis: Long = 0
 
-  private var webSocket: WebSocket = webSocketEngine.newWebSocket(serverUrl, httpHeaders, this)
+  private var webSocket: WebSocket
+  init {
+    val headers = if (httpHeaders.any { it.name.lowercase() == "sec-websocket-protocol" }) {
+      httpHeaders
+    } else {
+      httpHeaders + HttpHeader("Sec-WebSocket-Protocol", wsProtocol.name)
+    }
+    webSocket = webSocketEngine.newWebSocket(serverUrl, headers, this)
+  }
 
   val lastActiveMillis: Long
     get() = lock.withLock {


Test patch:
diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/network/WebSocketNetworkTransportTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/network/WebSocketNetworkTransportTest.kt
index d7314fe3d69..c72a2a0bb9d 100644
--- a/libraries/apollo-runtime/src/commonTest/kotlin/test/network/WebSocketNetworkTransportTest.kt
+++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/network/WebSocketNetworkTransportTest.kt
@@ -8,13 +8,6 @@ import com.apollographql.apollo3.exception.ApolloWebSocketClosedException
 import com.apollographql.apollo3.exception.DefaultApolloException
 import com.apollographql.apollo3.exception.SubscriptionOperationException
 import com.apollographql.apollo3.interceptor.addRetryOnErrorInterceptor
-import com.apollographql.mockserver.CloseFrame
-import com.apollographql.mockserver.MockServer
-import com.apollographql.mockserver.TextMessage
-import com.apollographql.mockserver.WebSocketBody
-import com.apollographql.mockserver.WebsocketMockRequest
-import com.apollographql.mockserver.awaitWebSocketRequest
-import com.apollographql.mockserver.enqueueWebSocket
 import com.apollographql.apollo3.mpp.Platform
 import com.apollographql.apollo3.mpp.platform
 import com.apollographql.apollo3.network.websocket.WebSocketNetworkTransport
@@ -25,6 +18,14 @@ import com.apollographql.apollo3.testing.FooSubscription.Companion.errorMessage
 import com.apollographql.apollo3.testing.FooSubscription.Companion.nextMessage
 import com.apollographql.apollo3.testing.connectionAckMessage
 import com.apollographql.apollo3.testing.internal.runTest
+import com.apollographql.mockserver.CloseFrame
+import com.apollographql.mockserver.MockServer
+import com.apollographql.mockserver.TextMessage
+import com.apollographql.mockserver.WebSocketBody
+import com.apollographql.mockserver.WebsocketMockRequest
+import com.apollographql.mockserver.awaitWebSocketRequest
+import com.apollographql.mockserver.enqueueWebSocket
+import com.apollographql.mockserver.headerValueOf
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.merge
@@ -41,6 +42,9 @@ class WebSocketNetworkTransportTest {
         .toFlow()
         .test {
           awaitConnectionInit()
+
+          assertEquals("graphql-transport-ws", serverReader.headers.headerValueOf("sec-websocket-protocol"))
+
           val operationId = serverReader.awaitSubscribe()
           repeat(5) {
             serverWriter.enqueueMessage(nextMessage(operationId, it))