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