1mod auth_context;
5#[cfg(test)]
6mod auth_tests;
7mod auth_token;
8mod capabilities;
9mod message_auth;
10mod message_delta;
11mod message_hosted;
12mod message_objects;
13mod message_pushpull;
14mod message_refs;
15mod message_status;
16mod native_pack;
17mod object_availability;
18mod object_graph;
19mod object_transfer;
20mod scope_match;
21
22pub use auth_context::AuthContext;
23pub use auth_token::{AuthToken, TokenScope};
24pub use capabilities::{
25 CAPABILITY_CHUNKED_TRANSFER, CAPABILITY_PACK_TRANSFER, CAPABILITY_PARTIAL_FETCH,
26 CAPABILITY_RESUMABLE_TRANSFER, Capabilities, CapabilitySet,
27};
28pub use message_auth::{AuthMethod, Permission};
29pub use message_delta::{DeltaData, RequestDelta};
30pub use message_hosted::{
31 CreateHostedGrant, CreateHostedRepository, CreateNamespace, DeleteHostedGrant,
32 DeleteHostedRepository, DeleteNamespace, HarnessIdentity, HostedGrantCreated,
33 HostedGrantDeleted, HostedGrantInfo, HostedGrantUpdated, HostedGrantsList, HostedNamespaceInfo,
34 HostedRepositoryInfo, ListHostedGrants, ListHostedNamespaces, ListHostedRepositories,
35 NamespaceCreated, NamespaceDeleted, NamespaceUpdated, NamespacesList, ProgressCheckpoint,
36 RepositoriesList, RepositoryCreated, RepositoryDeleted, RepositoryUpdated, SessionDiffSummary,
37 SessionReportEnvelope, TranscriptAttachmentRef, UpdateHostedGrant, UpdateHostedRepository,
38 UpdateNamespace, UsageTotals, WorktreeChangeBaseline,
39};
40pub use message_objects::{HaveObjects, ObjectData, ObjectRequest, SendObjects, WantObjects};
41pub use message_pushpull::{PullComplete, PushComplete};
42pub use message_refs::{HeadInfo, ListRefs, RefEntry, RefFilter, RefUpdated, RefsList, UpdateRef};
43pub use message_status::{Error, ErrorCode, Status, StatusCode};
44pub use native_pack::{
45 NativePackBundle, PackChunkState, build_native_pack, install_received_pack,
46 is_native_packable_object_type, native_pack_excluded_object_types, next_pack_chunk,
47 receive_pack_chunk,
48};
49pub use object_availability::{ObjectAvailabilityPlan, has_object, plan_object_availability};
50pub use object_graph::{
51 ObjectId, ObjectInfo, ObjectType, PlannedObject, StateClosureOptions, enumerate_state_closure,
52 enumerate_state_closure_plan, enumerate_state_closure_plan_with_options,
53 enumerate_state_closure_with_options, is_ancestor,
54};
55pub use object_transfer::{
56 MAX_PULL_DECODE_MESSAGE_SIZE, MAX_RECEIVED_REDACTIONS_BLOB_SIZE,
57 MAX_RECEIVED_STATE_VISIBILITY_BLOB_SIZE, check_received_transfer_blob_size, chunk_bounds,
58 chunk_count, chunk_offset, load_object_data, load_requested_object, store_received_object,
59};
60pub use scope_match::scope_contains;
61
62pub const DEFAULT_PORT: u16 = 8421;
64
65pub const PROTOCOL_VERSION: u32 = 1;
67
68pub const MAX_MESSAGE_SIZE: usize = 64 * 1024 * 1024;
70
71#[derive(Debug, thiserror::Error)]
73pub enum ProtocolError {
74 #[error("io error: {0}")]
75 Io(#[from] std::io::Error),
76
77 #[error("serialization error: {0}")]
78 Serialization(String),
79
80 #[error("message too large: {size} bytes (max {max})")]
81 MessageTooLarge { size: usize, max: usize },
82
83 #[error("invalid message type: {0}")]
84 InvalidMessageType(u8),
85
86 #[error("protocol version mismatch: server={server}, client={client}")]
87 VersionMismatch { server: u32, client: u32 },
88
89 #[error("capability not supported: {0}")]
90 CapabilityNotSupported(String),
91
92 #[error("authentication failed: {0}")]
93 AuthenticationFailed(String),
94
95 #[error("authorization failed: {0}")]
96 AuthorizationFailed(String),
97
98 #[error("object not found: {0}")]
99 ObjectNotFound(String),
100
101 #[error("invalid state: {0}")]
102 InvalidState(String),
103
104 #[error("remote error: {0}")]
105 Remote(String),
106
107 #[error("remote failure ({code:?}): {message}")]
108 RemoteFailure {
109 code: ErrorCode,
110 message: String,
111 details: Option<String>,
112 },
113
114 #[error("lock error: {0}")]
115 LockError(String),
116}
117
118impl From<rmp_serde::encode::Error> for ProtocolError {
119 fn from(e: rmp_serde::encode::Error) -> Self {
120 ProtocolError::Serialization(e.to_string())
121 }
122}
123
124impl From<rmp_serde::decode::Error> for ProtocolError {
125 fn from(e: rmp_serde::decode::Error) -> Self {
126 ProtocolError::Serialization(e.to_string())
127 }
128}
129
130impl From<objects::error::HeddleError> for ProtocolError {
131 fn from(e: objects::error::HeddleError) -> Self {
132 ProtocolError::Remote(e.to_string())
133 }
134}
135
136impl ProtocolError {
137 pub fn client_message(&self) -> String {
138 match self {
139 ProtocolError::Io(_) => "network error".to_string(),
140 ProtocolError::Serialization(_) => "protocol error".to_string(),
141 ProtocolError::MessageTooLarge { .. } => "message too large".to_string(),
142 ProtocolError::InvalidMessageType(_) => "protocol error".to_string(),
143 ProtocolError::VersionMismatch { .. } => "protocol version mismatch".to_string(),
144 ProtocolError::CapabilityNotSupported(_) => "capability not supported".to_string(),
145 ProtocolError::AuthenticationFailed(_) => "permission denied".to_string(),
146 ProtocolError::AuthorizationFailed(_) => "permission denied".to_string(),
147 ProtocolError::ObjectNotFound(_) => "object not found".to_string(),
148 ProtocolError::InvalidState(_) => "invalid request state".to_string(),
149 ProtocolError::Remote(_) => "internal server error".to_string(),
150 ProtocolError::RemoteFailure { message, .. } => message.clone(),
151 ProtocolError::LockError(_) => "internal server error".to_string(),
152 }
153 }
154
155 pub fn error_code(&self) -> ErrorCode {
156 match self {
157 ProtocolError::Io(_) => ErrorCode::Network,
158 ProtocolError::Serialization(_) => ErrorCode::Protocol,
159 ProtocolError::MessageTooLarge { .. } => ErrorCode::Protocol,
160 ProtocolError::InvalidMessageType(_) => ErrorCode::Protocol,
161 ProtocolError::VersionMismatch { .. } => ErrorCode::Protocol,
162 ProtocolError::CapabilityNotSupported(_) => ErrorCode::Protocol,
163 ProtocolError::AuthenticationFailed(_) => ErrorCode::PermissionDenied,
164 ProtocolError::AuthorizationFailed(_) => ErrorCode::PermissionDenied,
165 ProtocolError::ObjectNotFound(_) => ErrorCode::NotFound,
166 ProtocolError::InvalidState(_) => ErrorCode::InvalidArgument,
167 ProtocolError::Remote(_) => ErrorCode::Server,
168 ProtocolError::RemoteFailure { code, .. } => *code,
169 ProtocolError::LockError(_) => ErrorCode::Server,
170 }
171 }
172
173 pub fn to_wire_error(&self, details: Option<String>) -> Error {
174 Error {
175 code: self.error_code(),
176 message: self.client_message(),
177 details,
178 }
179 }
180}
181
182pub type Result<T> = std::result::Result<T, ProtocolError>;
183
184#[cfg(test)]
185mod tests {
186 use std::io;
187
188 use super::{ErrorCode, ProtocolError};
189
190 #[test]
191 fn protocol_error_public_mapping_is_stable() {
192 let cases = vec![
193 (
194 ProtocolError::Io(io::Error::new(io::ErrorKind::TimedOut, "timeout")),
195 "network error",
196 ErrorCode::Network,
197 ),
198 (
199 ProtocolError::Serialization("bad msgpack".to_string()),
200 "protocol error",
201 ErrorCode::Protocol,
202 ),
203 (
204 ProtocolError::MessageTooLarge { size: 65, max: 64 },
205 "message too large",
206 ErrorCode::Protocol,
207 ),
208 (
209 ProtocolError::InvalidMessageType(42),
210 "protocol error",
211 ErrorCode::Protocol,
212 ),
213 (
214 ProtocolError::VersionMismatch {
215 server: 2,
216 client: 1,
217 },
218 "protocol version mismatch",
219 ErrorCode::Protocol,
220 ),
221 (
222 ProtocolError::CapabilityNotSupported("pack-v2".to_string()),
223 "capability not supported",
224 ErrorCode::Protocol,
225 ),
226 (
227 ProtocolError::AuthenticationFailed("bad token".to_string()),
228 "permission denied",
229 ErrorCode::PermissionDenied,
230 ),
231 (
232 ProtocolError::AuthorizationFailed("missing grant".to_string()),
233 "permission denied",
234 ErrorCode::PermissionDenied,
235 ),
236 (
237 ProtocolError::ObjectNotFound("abc123".to_string()),
238 "object not found",
239 ErrorCode::NotFound,
240 ),
241 (
242 ProtocolError::InvalidState("bad resume".to_string()),
243 "invalid request state",
244 ErrorCode::InvalidArgument,
245 ),
246 (
247 ProtocolError::Remote("database unavailable".to_string()),
248 "internal server error",
249 ErrorCode::Server,
250 ),
251 (
252 ProtocolError::RemoteFailure {
253 code: ErrorCode::InvalidArgument,
254 message: "server supplied message".to_string(),
255 details: Some("remote details".to_string()),
256 },
257 "server supplied message",
258 ErrorCode::InvalidArgument,
259 ),
260 (
261 ProtocolError::LockError("ref locked".to_string()),
262 "internal server error",
263 ErrorCode::Server,
264 ),
265 ];
266
267 for (error, expected_message, expected_code) in cases {
268 assert_eq!(error.client_message(), expected_message);
269 assert_eq!(error.error_code(), expected_code);
270
271 let wire_error = error.to_wire_error(Some("trace id".to_string()));
272 assert_eq!(wire_error.code, expected_code);
273 assert_eq!(wire_error.message, expected_message);
274 assert_eq!(wire_error.details.as_deref(), Some("trace id"));
275 }
276 }
277}