1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! The typed wire semantics of the sing-box Clash API delay endpoint.
//!
//! sing-box answers `GET /proxies/{name}/delay?url={url}&timeout={ms}` with
//! one of its own typed answers. This module owns that classification, so the
//! outbound adapter never reads a status code or a message on its own and
//! never turns an answer the engine did not produce into a measurement.
//!
//! The answers of sing-box 1.13 and 1.14 are:
//!
//! | Status | Body | Meaning |
//! | --- | --- | --- |
//! | `200` | `{"delay": N}` | the engine measured a round trip through the outbound |
//! | `400` | `{"message": "Body invalid"}` | the engine refused the request |
//! | `401` / `403` | `{"message": ...}` | the controller refused the secret |
//! | `404` | `{"message": "Resource not found"}` | the engine does not know the outbound |
//! | `503` | `{"message": "An error occurred in the delay test"}` | the delay test failed |
//! | `504` | `{"message": "Timeout"}` | the engine's own delay test exceeded its deadline |
//!
//! The endpoint parses the timeout as a signed 16-bit millisecond count and
//! measures HTTPS targets only: a plain-HTTP target is replaced by the
//! engine's own URL test target. The driver therefore sends the requested
//! target unchanged and reports the measurement the engine publishes, and it
//! refuses a timeout the endpoint cannot carry before the request is sent.
use ErrorCode;
use Deserialize;
/// The largest delay timeout the sing-box delay endpoint accepts, in
/// milliseconds.
///
/// The endpoint parses the timeout as a signed 16-bit millisecond count, so a
/// larger value is refused before the request is sent.
pub const MAX_SINGBOX_DELAY_TIMEOUT_MS: u32 = i16MAX as u32;
/// The status the engine answers with when its own delay test exceeded the
/// deadline it was given.
const ENGINE_TIMEOUT_STATUS: u16 = 504;
/// The status the engine answers with when the delay test itself failed.
const ENGINE_TEST_FAILED_STATUS: u16 = 503;
/// The status the engine answers with when it does not know the outbound.
const ENGINE_UNKNOWN_OUTBOUND_STATUS: u16 = 404;
/// The status the engine answers with when it refuses the request.
const ENGINE_REFUSED_STATUS: u16 = 400;
/// The status the engine answers with when the controller refuses the secret.
const ENGINE_UNAUTHORIZED_STATUS: u16 = 401;
/// The status the engine answers with when the controller forbids the request.
const ENGINE_FORBIDDEN_STATUS: u16 = 403;
/// The status the engine answers with when its own handler failed.
const ENGINE_INTERNAL_ERROR_STATUS: u16 = 500;
/// The typed answer of the sing-box delay endpoint.
/// Classifies the engine's answer to one delay request.
///
/// A `200` answer carries the measurement; every other answer of the engine
/// carries its own typed error body. An answer the engine's delay endpoint
/// does not produce is never read as a measurement, a timeout or a refusal.
/// The neutral failure code one refused delay answer maps to.
///
/// The mapping follows the engine's own answers: a malformed request is the
/// caller's input, a refused secret is an authentication failure, an unknown
/// outbound is a stale observation and every other refusal is a network
/// failure of the engine's delay test.
/// The measurement the engine published.
/// The typed error body every refusal of the engine carries.