protocol

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2022 License: GPL-3.0 Imports: 3 Imported by: 7

README

protocol

This module contains functionality to implement the current HLHV cell-to-cell communication protocol.

Protocol Overview

The HLHV protocol is based on a client/server model, where a central server called the queen cell routes incoming HTTPS requests to different client cells. Client cells connect to the queen cell over a main socket called the leash, and maintain a second multiplexed connection made of several other sockets called bands. Bands are used for shoveling HTTP requests and responses back and forth at an alarming rate, and they are automatically created and discarded as needed. The leash is for sending messages that control and regulate the overall connection, such as mounting, un-mounting, and requesting more bands.

┌────────┐                   ┌─────────┐                   ┌────────┐
│        │ ◄──── Leash ────► │         │ ◄──── Leash ────► │        │
│        │                   │         │                   │        │
│  Cell  │ ◄───────────────► │  Queen  │ ◄───────────────► │  Cell  │
│        │ ◄──── Bands ────► │         │ ◄──── Bands ────► │        │
│        │ ◄───────────────► │         │ ◄───────────────► │        │
└────────┘                   └─────────┘                   └────────┘

Connection Initiation

All frames mentioned in this section are sent over the leash.

The queen listens for incoming connections on what is usually port 2001. Cells connect to this port over TLS, using the fsock socket framing protocol. The queen and client cell communicate by sending JSON encoded frames (at a later date, this will be replaced with HNBS), and frames containing arbitrary binary data.

To initiate the connection, the client cell must send a frame of type FrameIAm. This frame contains the connection kind (which must be ConnKindCell, aka 0x0), and the correct key.

If the key is correct, the queen will send a frame of type FrameAccept, containing the connection UUID, and the connection key. These will be used later by the cell in order to connect new bands. If the key is incorrect, or a timeout was reached, the queen should close the connection.

Before HTTP requests can be sent to the cell, it must first request to mount on the queen. To do this, it must send a frame of type FrameMount with the hostname and path it wants to mount on. If the path ends with a '/', the cell will receive requests for all paths under the path it has mounted on (except for paths that other cells are mounted on).

To un-mount, the client must send a frame of type FrameUnmount. This frame carries no data.

HTTPS Requests

All frames mentioned in this section are sent over bands.

Upon receiving an HTTPS request, the queen directs information about the request head to the proper cell in the form of a frame of type FrameHTTPReqHead. The request path is included in this information, but it starts at the cell's mountpoint. For example, a request for @/photos/dinosaurs/ to a cell mounted on @/photos/ is sent to the cell as /dinosaurs/.

This frame contains no request body data. If the client wants this data, it must ask for it by sending a frame of type FrameHTTPResWant, specifying the maximum length of the request body. The queen will then send the request body data in chunks via frames of type FrameHTTPReqBody, ending with a frame of type FrameHTTPReqHead. The cell is expected to stitch all body data frames together in order in which they are received.

Then, the client must send a frame of type FrameHTTPResHead, containing the HTTP status code of the response and a map containing headers. After this, the client must send the response body in chunks via frames of type FrameHTTPResBody, ending with a frame of type FrameHTTPResEnd.

Band Requests

When there are no available bands to handle an HTTP request, the queen will request that the client connect another band by sending a frame of type FrameNeedBand over the leash. This frame will contain the number of new bands to create. It is important that the client immediately respond to this request, because the queen waits until the band has connected to resume fulfilling the HTTP request.

The client should connect new bands in the same way as connecting as a cell, but by specifying a connection kind of ConnKindBand (aka 0x1), and specifying the connection UUID and connection key.

Documentation

Index

Constants

View Source
const (
	// these numbers are part of the protocol and incredibly importatnt. Do
	// not make this an enum with iota.
	ConnKindCell = 0x0
	ConnKindBand = 0x1
)

A connection can be for a cell, or a band. These constants store the connection kind numbers.

Variables

This section is empty.

Functions

func MarshalFrame

func MarshalFrame(
	frame Frame,
) (
	frameData []byte,
	err error,
)

MarshalFrame takes in a struct satisfying the Frame interface and endcodes it into a valid frame.

func WriteMarshalFrame

func WriteMarshalFrame(writer *fsock.Writer, frame Frame) (nn int, err error)

WriteMarshalFrame marshals and writes a Frame.

Types

type DataFrame

type DataFrame interface {
	GetData() []byte
}

DataFrame is a specific type of frame which can store arbitrary data.

type Frame

type Frame interface {
	Kind() FrameKind
}

Frame represents a singular block of data sent between cells.

type FrameAccept

type FrameAccept struct {
	Uuid string `json:"uuid"`
	Key  string `json:"key"`
}

FrameAccept is sent from the queen to the client cell, assigning the cell a UUID and session key that it can use to create bands later.

func (*FrameAccept) Kind

func (frame *FrameAccept) Kind() FrameKind

type FrameHTTPReqBody

type FrameHTTPReqBody struct {
	Data []byte
}

FrameHTTPReqBody is sent from the queen to the client cell after the cell asks for the HTTP body. It contains a single chunk of the body, and is often sent multiple times in a row. The cell should stitch these together until it receives FrameHTTPReqEnd.

func (*FrameHTTPReqBody) GetData

func (frame *FrameHTTPReqBody) GetData() []byte

These functions return the arbitrary data stored in data frames.

func (*FrameHTTPReqBody) Kind

func (frame *FrameHTTPReqBody) Kind() FrameKind

type FrameHTTPReqEnd

type FrameHTTPReqEnd struct{}

FrameHTTPReqEnd is sent from the queen to the client cell when there is no more HTTP body data left.

func (*FrameHTTPReqEnd) Kind

func (frame *FrameHTTPReqEnd) Kind() FrameKind

type FrameHTTPReqHead

type FrameHTTPReqHead struct {
	RemoteAddrReal string `json:"remoteAddrReal"`
	RemoteAddr     string `json:"remoteAddr"`
	Method         string `json:"method"`

	Scheme   string              `json:"scheme"`
	Host     string              `json:"host"`
	Port     int                 `json:"port"`
	Path     string              `json:"path"`
	Fragment string              `json:"fragment"`
	Query    map[string][]string `json:"query"`

	Proto      string `json:"proto"`
	ProtoMajor int    `json:"protoMajor"`
	ProtoMinor int    `json:"protoMinor"`

	Headers map[string][]string `json:"headers"`
	Cookies map[string][]string `json:"cookies"`
}

FrameHTTPReqHead is sent from the queen to the client cell when the queen receives an HTTP request and has determined that this cell should handle it. It contains extensive information about the request. If the cell wants the HTTP body to be sent to it, it must specifically request it.

func (*FrameHTTPReqHead) Kind

func (frame *FrameHTTPReqHead) Kind() FrameKind

type FrameHTTPResBody

type FrameHTTPResBody struct {
	Data []byte
}

FrameHTTPResBody is sent from the client cell to the queen. It contains a chunk of the HTTP response body data that should be written to the client. The queen, upon receiving this, should send it to the HTTP client. This frame should not precede FrameHTTPResHead.

func (*FrameHTTPResBody) GetData

func (frame *FrameHTTPResBody) GetData() []byte

func (*FrameHTTPResBody) Kind

func (frame *FrameHTTPResBody) Kind() FrameKind

type FrameHTTPResEnd

type FrameHTTPResEnd struct{}

FrameHTTPResEnd is sent from the client cell to the queen as a signal that the entirety of the HTTP response body has been sent, and the connection to the HTTP client should be closed.

func (*FrameHTTPResEnd) Kind

func (frame *FrameHTTPResEnd) Kind() FrameKind

type FrameHTTPResHead

type FrameHTTPResHead struct {
	StatusCode int                 `json:"statusCode"`
	Headers    map[string][]string `json:"headers"`
}

FrameHTTPResHead is sent from the client cell to the queen. It contains information such as the status code, and headers. It signals to the queen that it should begin responding to the HTTP client.

func (*FrameHTTPResHead) Kind

func (frame *FrameHTTPResHead) Kind() FrameKind

type FrameHTTPResWant

type FrameHTTPResWant struct {
	MaxSize int `json:"maxSize"`
}

FrameHTTPResWant is sent from the client cell to the queen as a request for the HTTP request body data. The cell must specify a maximum size. The queen, upon receiving this, will begin sending the HTTP body in chunks.

func (*FrameHTTPResWant) Kind

func (frame *FrameHTTPResWant) Kind() FrameKind

type FrameIAm

type FrameIAm struct {
	ConnKind int    `json:"connKind"`
	Uuid     string `json:"uuid"`
	Key      string `json:"key"`
}

FrameIAm is sent from the client cell to the queen in order to initiate a connection.

func (*FrameIAm) Kind

func (frame *FrameIAm) Kind() FrameKind

These functions return the frame kind from frame structs so that it can be determined which frame struct to cast a Frame interface to.

type FrameKind

type FrameKind byte

FrameKind determines what a Frame interface should be cast to.

const (

	// authentication/setup
	FrameKindIAm    FrameKind = 0x00
	FrameKindAccept FrameKind = 0x08

	// mounting
	FrameKindMount   FrameKind = 0x10
	FrameKindUnmount FrameKind = 0x11

	// resource requesting
	FrameKindNeedBand FrameKind = 0x20

	// http
	FrameKindHTTPReqHead FrameKind = 0x30
	FrameKindHTTPReqBody FrameKind = 0x31
	FrameKindHTTPReqEnd  FrameKind = 0x32

	FrameKindHTTPResWant FrameKind = 0x38
	FrameKindHTTPResHead FrameKind = 0x39
	FrameKindHTTPResBody FrameKind = 0x3A
	FrameKindHTTPResEnd  FrameKind = 0x3B
)

func ParseFrame

func ParseFrame(frameData []byte) (kind FrameKind, data []byte, err error)

ParseFrame splits a frame into its kind and its data. Unmarshaling should be conducted by the handler.

func ReadParseFrame

func ReadParseFrame(
	reader *fsock.Reader,
) (
	kind FrameKind,
	data []byte,
	err error,
)

ReadParseFrame reads a frame from an fsock reader and parses it.

type FrameMount

type FrameMount struct {
	Host string `json:"host"`
	Path string `json:"path"`
}

FrameMount is sent from the client cell to the queen. It contains information about the location the cell wants to mount on.

func (*FrameMount) Kind

func (frame *FrameMount) Kind() FrameKind

type FrameNeedBand

type FrameNeedBand struct {
	Count int `json:"count"`
}

FrameNeedBand is sent from the queen to the client cell when the queen detects that more bands are needed to keep the connection running smoothly. The cell is expected to immediately create and connect a new band to the queen.

func (*FrameNeedBand) Kind

func (frame *FrameNeedBand) Kind() FrameKind

type FrameUnmount

type FrameUnmount struct{}

FrameUnmount is sent from the client cell to the queen. Upon receiving this, the queen will unmount the cell from its current mount point.

func (*FrameUnmount) Kind

func (frame *FrameUnmount) Kind() FrameKind

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL