OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer / liboverlay / include / overlay / transport.h

101 lines · c ref:
#ifndef HYPEROVERLAY_TRANSPORT_H
#define HYPEROVERLAY_TRANSPORT_H

// The seam between the overlay protocol and whatever carries it.
//
// WHY THIS EXISTS RATHER THAN INCLUDING PROTOMUX DIRECTLY. liboverlay is meant to
// lift out by moving the directory, so it must not reach into
// hyperswarm-peer/src/ — and Protomux lives there. Inverting the dependency keeps
// the library standalone: liboverlay says "send message N with these bytes", and
// the host supplies something that can. hyperswarm-peer provides a Protomux
// adapter; the tests provide an in-memory loopback.
//
// The second payoff is that the protocol becomes testable WITHOUT a socket. The
// JS reference harness does the same thing — two overlays over an in-memory
// duplex pair — which is why its transcripts are a usable reference here.
//
// MESSAGE IDS ARE SLOT ORDER, NOT ARBITRARY. Protomux numbers channel messages by
// registration order, and hyper-overlay/lib/protocol.js registers them in exactly
// this sequence. An adapter must register in the same order or every message
// lands in the wrong handler on a peer that did.

#include <cstdint>
#include <cstddef>
#include <vector>

namespace hyperoverlay {

enum class MsgId : uint8_t {
	SyncState = 0,
	FileOffer = 1,
	FileRequest = 2,
	ChunkHashes = 3,
	ChunkNeed = 4,
	ChunkData = 5,
	ChunkCancel = 6,
	TransferComplete = 7,
	Conflict = 8,
	TreeRequest = 9,
	TreeResponse = 10,
	ContentRequest = 11,
	TransferControl = 12,
	TransferProgress = 13,
	COUNT = 14
};

inline const char *msg_name(MsgId id) {
	switch (id) {
		case MsgId::SyncState: return "syncState";
		case MsgId::FileOffer: return "fileOffer";
		case MsgId::FileRequest: return "fileRequest";
		case MsgId::ChunkHashes: return "chunkHashes";
		case MsgId::ChunkNeed: return "chunkNeed";
		case MsgId::ChunkData: return "chunkData";
		case MsgId::ChunkCancel: return "chunkCancel";
		case MsgId::TransferComplete: return "transferComplete";
		case MsgId::Conflict: return "conflict";
		case MsgId::TreeRequest: return "treeRequest";
		case MsgId::TreeResponse: return "treeResponse";
		case MsgId::ContentRequest: return "contentRequest";
		case MsgId::TransferControl: return "transferControl";
		case MsgId::TransferProgress: return "transferProgress";
		default: return "?";
	}
}

// One connected peer. The host implements this over Protomux (or anything else);
// the protocol driver only ever calls send().
class Transport {
public:
	virtual ~Transport() = default;
	// Returns whether the carrier can take more RIGHT NOW. False does not mean the
	// frame was dropped — it was accepted and queued — it means the write buffer is
	// full and the sender should stop producing until on_writable fires.
	//
	// This return value is the whole point of step 6. libudx already reports it
	// (udx_stream_write returns 0 at the high watermark) and every layer above was
	// throwing it away, so a chunk pump had no way to know it was burying the
	// connection. The frames it buries are not only its own: sibling protocols
	// share this Noise stream — the game's own RPC channel, DHT traffic — and an
	// unthrottled serve starves them.
	virtual bool send(MsgId id, const std::vector<uint8_t> &frame) = 0;
	// Stable identifier for the remote, used for logging and for keying
	// per-peer state. Content is opaque to the protocol.
	virtual const char *peer_label() const { return "peer"; }

	// Smoothed round-trip time, if the carrier knows one. The scheduler sizes each
	// peer's request window from it: a far peer needs more requests outstanding to
	// keep the link busy, a near one does not.
	//
	// ZERO MEANS "NO SIGNAL", NOT "INSTANT" — and the distinction is load-bearing.
	// The reference reads it off the udx stream and falls back to a modest fixed
	// window when the stream is not udx (an in-memory pair, a relay). Returning 0
	// selects that fallback; returning a real 0ms would ask for the minimum window
	// on the fastest link, which is backwards.
	virtual uint32_t rtt_ms() const { return 0; }
};

} // namespace hyperoverlay

#endif // HYPEROVERLAY_TRANSPORT_H