Skip to content

Commit e118a91

Browse files
committed
Update PortForward design to reflect current design
1 parent d67e0ce commit e118a91

File tree

2 files changed

+22
-79
lines changed

2 files changed

+22
-79
lines changed

keps/sig-api-machinery/4006-transition-spdy-to-websockets/README.md

Lines changed: 22 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ tags, and then generate with `hack/update-toc.sh`.
9494
- [Proposal: New <code>RemoteCommand</code> Sub-Protocol Version - <code>v5.channel.k8s.io</code>](#proposal-new-remotecommand-sub-protocol-version---v5channelk8sio)
9595
- [Proposal: API Server RemoteCommand <code>StreamTranslatorProxy</code>](#proposal-api-server-remotecommand-streamtranslatorproxy)
9696
- [Background: <code>PortForward</code> Subprotocol](#background-portforward-subprotocol)
97-
- [Proposal: New <code>PortForward</code> Subprotocol Version - <code>v2.portforward.k8s.io</code>](#proposal-new-portforward-subprotocol-version---v2portforwardk8sio)
98-
- [Proposal: API Server PortForward <code>StreamTranslatorProxy</code>](#proposal-api-server-portforward-streamtranslatorproxy)
97+
- [Proposal: New <code>PortForward</code> Tunneling Subprotocol Version - <code>v2.portforward.k8s.io</code>](#proposal-new-portforward-tunneling-subprotocol-version---v2portforwardk8sio)
98+
- [Proposal: API Server PortForward -- Stream Tunnel Proxy](#proposal-api-server-portforward----stream-tunnel-proxy)
9999
- [Pre-GA: Kubelet <code>StreamTranslatorProxy</code>](#pre-ga-kubelet-streamtranslatorproxy)
100100
- [Test Plan](#test-plan)
101101
- [Prerequisite testing updates](#prerequisite-testing-updates)
@@ -505,83 +505,26 @@ $ curl http://localhost:8080/index.html
505505
The nginx HTTP response is returned over the same data stream. Once the subrequest is
506506
complete, both streams are closed and removed.
507507

508-
### Proposal: New `PortForward` Subprotocol Version - `v2.portforward.k8s.io`
509-
510-
In order to implement `PortForward` over WebSockets, we propose the client attempt to
511-
upgrade the connection to WebSockets with a new `v2.portforward.k8s.io` subprotocol
512-
version. This new version will implement stream functionality over WebSockets
513-
which currently exists in SPDY but not WebSockets. Specifically, the WebSockets streams
514-
will implement:
515-
516-
1. StreamCreate signal: A signal to the other WebSockets endpoint that a
517-
stream has been created. This communication will also contain the headers used to
518-
create the stream.
519-
2. StreamClose signal: A signal to the other WebSockets endpoint that the stream
520-
has been half-closed, and will not allow any further writing on the stream from
521-
the closed end.
522-
3. Larger stream identifier space: In order to accommodate numerous concurrent
523-
streams from many posssible portforward subrequests, we will need a large stream
524-
identifier space. It appears that SPDY implements 2^31 possible stream identifiers
525-
and we will provide four bytes for the WebSockets stream identifier to match SPDY.
526-
The size of these four bytes should not be material, since the size of the
527-
WebSockets buffer is much larger (32KB). But if we need to reduce the size of
528-
the stream identifier, we can use a `varint` instead of a `uint32`.
529-
530-
The WebSockets stream functionality will be implemented by encoding and decoding
531-
stream headers within the WebSockets data message. The stream header struct will
532-
look like:
533-
```
534-
// wsStreamHeader contains the data at the beginning of a websocket binary message.
535-
type wsStreamHeader struct {
536-
MessageType byte // Create, Close, or Data
537-
StreamID uint32 // or varint if we need to optimize
538-
// Headers are only included in a Create message type. Well-known keys are:
539-
// 1. StreamType: DataStream or ErrorStream.
540-
// 2. RequestID: A unique identifier for the subrequest.
541-
// 3. Port: The remote port the stream is forwarding data to.
542-
Headers http.Header
543-
}
544-
```
545-
546-
### Proposal: API Server PortForward `StreamTranslatorProxy`
547-
548-
![PortForward Stream Translator Proxy](./portforward-stream-translator-proxy.png)
549-
550-
Updated steps detailing **requests** and **subrequests** for the proposed `StreamTranslatorProxy`.
551-
552-
1. `kubectl port-forward` makes a **request** to the API Server to upgrade to a WebSockets
553-
streaming connection. At the API Server, the WebSockets connection is upgraded and terminated,
554-
while a legacy SPDY connection is created upstream to the container runtime.
555-
2. An arbitrary number of subsequent (and possibly concurrent) client **subrequests** can be made over
556-
this previously established WebSockets connection. Example: `curl http://localhost:8080/index.html`.
557-
3. Each of these **subrequests** creates two streams over the connection (a uni-directional
558-
error stream and a bi-directional data stream) between the client and the API Server.
559-
4. The API Server in turn creates a one-to-one correspondence between the WebSockets streams
560-
and upstream SPDY streams. The WebSocket streams are connected to SPDY streams by goroutines
561-
copying data between them. So each **subrequest** spawns four goroutines to service the two
562-
streams (one for the **subrequest** itself, as well as three to copy stream data in each
563-
direction).
564-
5. All the resources associated with the **subrequest** are reclaimed once the **subrequest**
565-
is completed.
566-
567-
Similar to `RemoteCommand`, `PortForward` will also have a `StreamTranslatorProxy`
568-
within the API server to route data from WebSocket streams onto upstream, legacy
569-
SPDY streams. The new portforward `StreamTranslatorProxy` will handle requests
570-
for WebSockets connection upgrades with a `v2.portforward.k8s.io` header. The
571-
portforward `StreamTranslatorProxy` will initially attempt to create an upstream
572-
SPDY connection to the Kubelet using the legacy `v1.portforward.k8s.io` header.
573-
If successful, the `StreamTranslatorProxy` will create a server-side WebSockets
574-
connection, returning `101 Switching Protocols` to the WebSockets client. The
575-
server-side WebSockets connection will handle the `StreamCreate` and `StreamClose`
576-
signals, as well as de-multiplexing WebSocket streams using the passed stream
577-
identifier. Upon receiving a `StreamCreate` signal on the server-side of the
578-
WebSockets connection, a WebSocket stream will be created and queued onto a
579-
stream create channel. On the other end of the channel, the `StreamTranslatorProxy`
580-
will create a SPDY stream to associate with the WebSockets stream, using headers
581-
from the WebSockets stream. And if both portforward request streams have been
582-
created (data and error), then streaming will commence between the WebSockets
583-
and SPDY streams. Upon completion, the streams will be closed and removed. The
584-
`StreamClose` signal will be used to determine if the streaming has completed.
508+
### Proposal: New `PortForward` Tunneling Subprotocol Version - `v2.portforward.k8s.io`
509+
510+
We propose a new PortForward version `v2.portforward.k8s.io`, which identifies upgrade
511+
requests which require tunneling. PortForward tunneling transparently encodes and
512+
decodes SPDY framed messages into (and out of) the payload of a WebSocket message.
513+
This tunneling is implemented on the client by substituting a WebSocket connection
514+
(which implements the `net.Conn` interface) into the constructor of a SPDY client.
515+
The SPDY client reads and writes its messages into and out of this connection. These
516+
SPDY messages are then encoded or decoded into and out of the WebSocket message payload.
517+
518+
### Proposal: API Server PortForward -- Stream Tunnel Proxy
519+
520+
At the API Server, tunneling is implemented by sending different parameters into the
521+
`UpgradeAwareProxy`. If the new subprotocol version `v2.portforward.k8s.io` is requested,
522+
the `UpgradeAwareProxy` is called with a new `tunnelingResponseWriter`. This `ResponseWriter`
523+
contains a tunneling WebSocket connection, which is returned when the connection is
524+
hijacked. And this tunneling WebSocket connection encodes and decodes SPDY messages
525+
as the downstream connection within the dual concurrent `io.Copy` proxying goroutines.
526+
The upstream connection is the same SPDY connection to the container (through the
527+
Kubelet and CRI).
585528

586529
### Pre-GA: Kubelet `StreamTranslatorProxy`
587530

0 commit comments

Comments
 (0)