Repository: square/okhttp
File: okhttp/src/main/java/okhttp3/internal/ws/RealWebSocket.kt (Kotlin)

Code before:
        .header("Sec-WebSocket-Version", "13")
        .build()
    call = RealCall(webSocketClient, request, forWebSocket = true)
    call!!.enqueue(object : Callback {
      override fun onResponse(call: Call, response: Response) {
        val exchange = response.exchange
        val streams: Streams
        try {
          checkUpgradeSuccess(response, exchange)
          streams = exchange!!.newWebSocketStreams()
          // TODO(jwilson): use request & response headers to negotiate extensions.
          extensions = WebSocketExtensions()
        } catch (e: IOException) {
          exchange?.webSocketUpgradeFailed()
          failWebSocket(e, response)
          response.closeQuietly()
          return
        }

        // Process all web socket messages.
        try {
          val name = "$okHttpName WebSocket ${request.url.redact()}"
          initReaderAndWriter(name, streams)
          listener.onOpen(this@RealWebSocket, response)
          loopReader()
        } catch (e: Exception) {
          failWebSocket(e, null)
        }
      }

      override fun onFailure(call: Call, e: IOException) {
        failWebSocket(e, null)
      }
    })
  }

  @Throws(IOException::class)
  internal fun checkUpgradeSuccess(response: Response, exchange: Exchange?) {
    if (response.code != 101) {
      throw ProtocolException(
          "Expected HTTP 101 response but was '${response.code} ${response.message}'")
    }

    val headerConnection = response.header("Connection")
    if (!"Upgrade".equals(headerConnection, ignoreCase = true)) {
      throw ProtocolException(
          "Expected 'Connection' header value 'Upgrade' but was '$headerConnection'")
    }

    val headerUpgrade = response.header("Upgrade")
    if (!"websocket".equals(headerUpgrade, ignoreCase = true)) {

Review comment (bug):
@swankjesse  
Is there any reason not to use failWebSocket() here and return?
If not I am happy to submit a pr to fix this TODO

Code after:
        .protocols(ONLY_HTTP1)
        .build()
    val request = originalRequest.newBuilder()
        .header("Upgrade", "websocket")
        .header("Connection", "Upgrade")
        .header("Sec-WebSocket-Key", key)
        .header("Sec-WebSocket-Version", "13")
        .header("Sec-WebSocket-Extensions", "permessage-deflate")
        .build()
    call = RealCall(webSocketClient, request, forWebSocket = true)
    call!!.enqueue(object : Callback {
      override fun onResponse(call: Call, response: Response) {
        val exchange = response.exchange
        val streams: Streams
        try {
          checkUpgradeSuccess(response, exchange)
          streams = exchange!!.newWebSocketStreams()
        } catch (e: IOException) {
          exchange?.webSocketUpgradeFailed()
          failWebSocket(e, response)
          response.closeQuietly()
          return
        }

        // Apply the extensions. If they're unacceptable initiate a graceful shut down.
        // TODO(jwilson): Listeners should get onFailure() instead of onClosing() + onClosed(1010).
        val extensions = WebSocketExtensions.parse(response.headers)
        this@RealWebSocket.extensions = extensions
        if (!extensions.isValid()) {
          synchronized(this@RealWebSocket) {
            messageAndCloseQueue.clear() // Don't transmit any messages.
            close(1010, "unexpected Sec-WebSocket-Extensions in response header")
          }
        }

        // Process all web socket messages.
        try {
          val name = "$okHttpName WebSocket ${request.url.redact()}"
          initReaderAndWriter(name, streams)
          listener.onOpen(this@RealWebSocket, response)
          loopReader()
        } catch (e: Exception) {
          failWebSocket(e, null)
        }
      }

      override fun onFailure(call: Call, e: IOException) {
        failWebSocket(e, null)
      }
    })
  }