Files
Mateusz Najdek f8bd2d22cc Release v1.2.4 (#13)
* Add websocket stream debug telemetry

* Close idle WebSocket upload listener and refresh on demand

* Schedule websocket uploads around installer demand

* Pipeline websocket uploads with two segments in flight

* Keep the latest 20 stream and SMB debug logs per type

* Prepare v1.2.4 release notes and version
2026-09-23 19:37:19 +02:00

139 lines
6.4 KiB
C

#ifndef WS_UPLOAD_H
#define WS_UPLOAD_H
/* PKG Manager - Direct-install WebSocket upload (NEW, ISOLATED module).
*
* Live RAM streaming: a browser pushes .pkg bytes and the PS5 installer
* pulls them concurrently through the virtual stream layer. NOTHING is
* stored on disk: bytes live in the ws_stream ring (RAM) between writer
* and readers. See include/ws_stream.h.
*
* Transport + protocol live here (handshake, framing, listener on :18842,
* REST session ops); all byte storage lives in ws_stream.c.
*
* Design rules:
* - All symbols prefixed ws_direct_*. No constructor/destructor attrs.
* - No threads/sockets at load or at installer_init time. The listener
* starts lazily via ws_direct_ensure_listener() (first REST/WS use) or
* explicitly in tests via ws_direct_listener_start().
* - Single active upload session, single browser: seeks are delivered to
* the authenticated uploader; a second owner or socket is rejected.
* - Explicit per-segment addressing: every binary message belongs to the
* segment named by the preceding {"op":"seg"} text frame, so the
* browser may send (and resend) in any order, including far seeks that
* jump ahead of the baseline cursor.
* - The conn worker never blocks on storage. Segment stores go through
* ws_live_try_write: 0 stored (reply "ack"), -2 ring-busy (reply
* "busy", browser requeues and retries), anything else (reply
* "error", fatal). The socket therefore keeps flowing even when the
* ring is momentarily full of unserved data.
* - No dependency on libmicrohttpd (host tests link this file without MHD).
*/
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define WS_DIRECT_DEFAULT_PORT 18842
#define WS_DIRECT_CHUNK_DEFAULT (1024 * 1024)
#define WS_DIRECT_SESSION_ID_MAX 64
#define WS_DIRECT_FILENAME_MAX 256
#define WS_DIRECT_MAX_TOTAL (256ULL * 1024 * 1024 * 1024) /* sanity cap */
#define WS_DIRECT_ICON_MAX (10 * 1024 * 1024)
/* Create a live session. out_session_id gets a hex id.
* Returns 0 on ok, -2 if another session is active, -1 on error.
* Starts the listener lazily. */
int ws_direct_init_session(const char *filename, uint64_t total_size,
char *out_session_id, size_t sid_max);
/* Browser-owned session: owner is a random per-tab token, sid is required
* for resume. A second tab cannot claim a session by filename and size. */
int ws_direct_init_owned(const char *filename, uint64_t total_size,
const char *owner, const char *resume_sid,
char *out_session_id, size_t sid_max);
int ws_direct_owner_matches(const char *owner, const char *sid);
int ws_direct_cancel_owned(const char *owner, const char *sid);
void ws_direct_set_metadata(const char *owner, const char *sid,
const char *title, const char *title_id,
const char *version, const char *kind);
int ws_direct_get_metadata(const char *sid, char *title, size_t title_max,
char *title_id, size_t id_max, char *version,
size_t version_max, char *kind, size_t kind_max);
/* Session icon stays in RAM and is served to all browser sessions. The
* returned icon copy belongs to the caller; free it after use. */
int ws_direct_set_icon(const char *owner, const char *sid,
const uint8_t *png, size_t size);
int ws_direct_get_icon(const char *sid, uint8_t **out_png, size_t *out_size);
/* Append a chunk at the current offset. Must be in-order; may block on
* reader backpressure. Returns 0 on ok, -3 on offset mismatch
* (*out_expected gets server offset), -1 on abort/timeout/error. */
int ws_direct_write_chunk(uint64_t offset, const void *data, size_t len,
uint64_t *out_expected);
/* Mark the upload byte-complete (requires received == total). out_uri gets
* the "live:<id>" URI to hand to POST /api/install. Session stays active
* for readers until the install ends (abort/destroy there). */
int ws_direct_finish_session(char *out_uri, size_t uri_max);
/* Abort + free the session (idempotent; safe with readers attached —
* the free then waits for the last detach). */
void ws_direct_cancel_session(void);
/* Reset everything (for tests). */
void ws_direct_reset_for_tests(void);
int ws_direct_session_active(void);
/* JSON status (live fields: header_ready, served, ring). 0 on ok. */
int ws_direct_get_status(char *out_json, size_t max);
/* ---- RFC6455 helpers (no sockets; pure encode/decode) ---- */
/* Compute Sec-WebSocket-Accept for a client key. Returns 0 on ok. */
int ws_direct_handshake_accept_key(const char *client_key,
char *out_accept, size_t max);
/* Encode one server-to-client frame (unmasked) into out (out_max bytes).
* Returns frame length, or -1 if out too small. */
int ws_direct_frame_encode_server(unsigned char opcode,
const unsigned char *payload, size_t plen,
unsigned char *out, size_t out_max);
/* Encode one client-to-browser-style frame (masked, random mask) — used by
* the host test client. Returns frame length, or -1 if out too small. */
int ws_direct_frame_encode_client(unsigned char opcode,
const unsigned char *payload, size_t plen,
unsigned char *out, size_t out_max);
/* Decode one frame from buf[0..buflen). On success returns bytes consumed
* (>0), fills *out_opcode, *out_fin, *out_pay_off, *out_pay_len. Returns 0
* if more bytes are needed, -1 on protocol error. Unmasks in place when
* the frame was masked. */
int ws_direct_frame_decode(unsigned char *buf, size_t buflen,
unsigned char *out_opcode, int *out_fin,
size_t *out_pay_off, size_t *out_pay_len);
/* ---- Listener (raw-socket WS server, LAN-facing) ---- */
/* Start listener on port (0.0.0.0). 0 = already running. */
int ws_direct_listener_start(int port);
/* Ensure listener is running on the default port (lazy boot for daemon). */
int ws_direct_ensure_listener(void);
void ws_direct_listener_stop(void);
/* Close the listener after the session and all WebSocket clients are gone.
* Returns 1 if it closed the listener, 0 otherwise. */
int ws_direct_listener_stop_if_idle(void);
int ws_direct_listener_running(void);
int ws_direct_listener_port(void);
#ifdef __cplusplus
}
#endif
#endif /* WS_UPLOAD_H */