Skip to main content

KeepAliveServer

KeepAliveServer is the server-side peer for KeepAliveClient. It decodes upstream keepalive frames from the client side, forwards normal payloads to next, answers ping frames with pong frames, and wraps downstream payloads in the same frame format on the way back.

Use it only as the matching peer for KeepAliveClient. By itself, it expects framed upstream input and will not understand ordinary unframed payload bytes.

KeepAliveServer is a normal composable tunnel. It does not create sockets, does not create or destroy line_t objects, and does not own any periodic timer.

Typical Placement

Direct in-process test:

TesterClient -> KeepAliveClient -> KeepAliveServer -> TesterServer

Two-server TCP layout:

client side: TcpListener -> KeepAliveClient -> TcpConnector
server side: TcpListener -> KeepAliveServer -> TcpConnector

Any tunnels between the client and server must preserve the keepalive frame bytes exactly.

What It Does

  • Initializes local line state on upstream Init.
  • Buffers upstream framed bytes until complete frames are available.
  • Decodes upstream normal frames and forwards their payload to next.
  • Answers upstream ping frames by sending downstream pong frames.
  • Ignores upstream pong frames.
  • Drops empty normal frames and unknown frame kinds.
  • Encodes downstream payloads as normal keepalive frames and sends them to the previous node.
  • Splits downstream payloads larger than 65534 bytes into multiple normal frames.
  • Destroys local line state and forwards the received directional Finish.

The server does not have its own keepalive timer. Periodic ping generation is owned by KeepAliveClient.

Configuration Example

{
"name": "ka-server",
"type": "KeepAliveServer",
"settings": {},
"next": "service"
}

Matching client side:

{
"name": "ka-client",
"type": "KeepAliveClient",
"settings": {
"ping-interval": 60000
},
"next": "transport"
}

Required Fields

Top-level fields:

FieldTypeDescription
namestringUser-chosen node name. Must be unique inside the config file.
typestringMust be exactly "KeepAliveServer".
settingsobjectOptional. The current implementation has no server-specific settings.
nextstringRequired in normal use. The next node receives decoded upstream payloads.

KeepAliveServer must have a previous node and must be reached by a matching KeepAliveClient.

Settings

KeepAliveServer currently has no meaningful settings. Keepalive timing is configured on KeepAliveClient with settings.ping-interval.

Frame Format

The server uses the same 3-byte prefix as the client:

BytesFieldDescription
0..1body lengthUnsigned 16-bit big-endian integer. This length includes the 1-byte frame kind plus any payload bytes.
2frame kind1 normal payload, 2 ping, or 3 pong.

Frame kinds:

KindMeaningBody payload
1normal payloadOriginal WaterWall payload bytes.
2pingEmpty in normal keepalive traffic.
3pongEmpty response to a ping frame.

Because the body length is 16-bit and includes the kind byte, the maximum payload carried by one normal frame is 65534 bytes. Larger downstream payloads are split into multiple frames.

Direction Behavior

Incoming callbackBehavior
upstream InitInitializes the read buffer, then forwards Init to next.
upstream PayloadTreats bytes as framed input from KeepAliveClient; forwards decoded normal payloads to next.
upstream Est / Pause / ResumeForwards to next.
upstream FinishDestroys local state, then forwards Finish to next.
downstream PayloadEncodes the payload into one or more normal frames and forwards them to the previous node. Empty payloads are recycled.
downstream Est / Pause / ResumeForwards to the previous node.
downstream FinishDestroys local state, then forwards Finish to the previous node.
downstream InitDisabled; reaching this callback is treated as a fatal invalid chain flow.

Framed Input Handling

Upstream bytes are accumulated in a buffer_stream_t. This means a frame can arrive split across multiple payload callbacks, or multiple frames can arrive in one callback.

The decoder behavior is conservative:

  • waits for more bytes when the prefix or full body has not arrived yet
  • clears the read stream when a frame body length smaller than 1 is seen
  • drops empty normal frames
  • drops unknown frame kinds
  • clears the read stream if buffered upstream input grows beyond 131074 bytes

Buffer And Padding Notes

KeepAliveServer prepends the 3-byte frame prefix while encoding downstream payloads, so its node metadata advertises:

required_padding_left = 3
layer_group = kNodeLayerAnything

If a frame buffer does not have enough left padding for the prefix, the frame is dropped and the buffer is recycled. In a correctly composed chain, WaterWall's chain padding calculation should reserve this space.

When the current line is a worker packet line, the server checks that the framed output does not exceed kMaxAllowedPacketLength. If payload + 3 would exceed that limit, the frame is dropped.

Practical Notes

  • Pair this node with KeepAliveClient; it is not useful by itself.
  • Ping and pong frames are internal to the KeepAlive pair and are not delivered as payload to surrounding nodes.
  • The server answers pings, but it does not measure client health or enforce a pong timeout.
  • This node does not encrypt, authenticate, compress, or rewrite payload bytes.
  • It is safest around transports that preserve the framed bytes between the client and server.