/** std/net - local networking helpers for scripts. */
type UnixSocketJsonOptions = {timeout_ms?: int, max_response_bytes?: int}
type UnixSocketJsonResult = {
ok: bool,
status: string,
path: string,
duration_ms: int,
response?: any,
raw_response?: string,
bytes_read?: int,
error?: string,
}
/**
* Send one JSON request line to a Unix-domain socket and read one JSON response line.
*
* Expected readiness failures return `{ok: false, status, error?}` instead of throwing.
*
* @effects: [network]
* @errors: []
* @example: unix_socket_json_request(harness.net, path, {id: "ping", method: "ping"})
*/
pub fn unix_socket_json_request(
net: HarnessNet,
path: string,
request,
options: UnixSocketJsonOptions = {},
) -> UnixSocketJsonResult {
return net.unix_socket_json_request(path, request, options ?? {})
}
fn __http_date_pad2(value) {
const text = to_string(to_int(value) ?? 0)
if len(text) == 1 {
return "0" + text
}
return text
}
fn __http_date_month_number(name) {
const months = {
apr: "04",
aug: "08",
dec: "12",
feb: "02",
jan: "01",
jul: "07",
jun: "06",
mar: "03",
may: "05",
nov: "11",
oct: "10",
sep: "09",
}
return months[lowercase(name)]
}
fn __http_date_ms(clock: HarnessClock, raw) {
const matches = regex_captures(
"^[A-Za-z]{3},\\s+(\\d{1,2})\\s+([A-Za-z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+GMT$",
raw,
)
if len(matches) == 0 {
return nil
}
const parts = matches[0].groups
const month = __http_date_month_number(parts[1])
if month == nil {
return nil
}
const iso = parts[2] + "-" + month + "-" + __http_date_pad2(parts[0])
+ "T"
+ parts[3]
+ ":"
+ parts[4]
+ ":"
+ parts[5]
+ "Z"
const parsed = try {
date_parse(iso)
}
if is_err(parsed) {
return nil
}
const delta_seconds = to_float(unwrap(parsed)) - clock.timestamp()
if delta_seconds <= 0.0 {
return 0
}
return to_int(delta_seconds * 1000.0)
}
/**
* Parse an HTTP `Retry-After` header value into a wait in milliseconds.
*
* Accepts both RFC 9110 forms — delay seconds (integer or fractional) and an
* HTTP-date — returning `0` for values already in the past and `nil` when the
* value is empty or unparseable. One owner for every retry surface: hand-rolled
* copies that assumed the numeric form crashed on legal date headers.
*
* @effects: []
* @errors: []
* @example: http_retry_after_ms(harness.clock, "120")
*/
pub fn http_retry_after_ms(clock: HarnessClock, value: any) -> int? {
const raw = trim(to_string(value ?? ""))
if raw == "" {
return nil
}
const seconds = to_float(raw)
if seconds != nil {
if seconds <= 0.0 {
return 0
}
return to_int(seconds * 1000.0)
}
const parsed = try {
date_parse(raw)
}
if is_ok(parsed) {
const delta_seconds = to_float(unwrap(parsed)) - clock.timestamp()
if delta_seconds <= 0.0 {
return 0
}
return to_int(delta_seconds * 1000.0)
}
return __http_date_ms(clock, raw)
}