11//! A module for Warg registry API clients.
22
3- use anyhow:: { anyhow, bail , Context , Result } ;
3+ use anyhow:: { anyhow, Result } ;
44use bytes:: Bytes ;
55use futures_util:: { Stream , TryStreamExt } ;
6- use reqwest:: { Body , IntoUrl , Response , StatusCode , Url } ;
6+ use reqwest:: { Body , IntoUrl , Response , StatusCode } ;
77use serde:: de:: DeserializeOwned ;
88use thiserror:: Error ;
9- use url:: Host ;
109use warg_api:: v1:: {
1110 fetch:: { FetchError , FetchLogsRequest , FetchLogsResponse } ,
1211 package:: {
@@ -27,6 +26,8 @@ use warg_transparency::{
2726 map:: MapProofBundle ,
2827} ;
2928
29+ use crate :: registry_url:: RegistryUrl ;
30+
3031/// Represents an error that occurred while communicating with the registry.
3132#[ derive( Debug , Error ) ]
3233pub enum ClientError {
@@ -122,71 +123,28 @@ async fn into_result<T: DeserializeOwned, E: DeserializeOwned + Into<ClientError
122123/// Represents a Warg API client for communicating with
123124/// a Warg registry server.
124125pub struct Client {
125- url : Url ,
126+ url : RegistryUrl ,
126127 client : reqwest:: Client ,
127128}
128129
129130impl Client {
130131 /// Creates a new API client with the given URL.
131132 pub fn new ( url : impl IntoUrl ) -> Result < Self > {
132- let url = Self :: validate_url ( url) ?;
133+ let url = RegistryUrl :: new ( url) ?;
133134 Ok ( Self {
134135 url,
135136 client : reqwest:: Client :: new ( ) ,
136137 } )
137138 }
138139
139140 /// Gets the URL of the API client.
140- pub fn url ( & self ) -> & str {
141- self . url . as_str ( )
142- }
143-
144- /// Parses and validates the given URL.
145- ///
146- /// Returns the validated URL on success.
147- pub fn validate_url ( url : impl IntoUrl ) -> Result < Url > {
148- // Default to a HTTPS scheme if none is provided
149- let url: Url = if !url. as_str ( ) . contains ( "://" ) {
150- Url :: parse ( & format ! ( "https://{url}" , url = url. as_str( ) ) )
151- . context ( "failed to parse registry server URL" ) ?
152- } else {
153- url. into_url ( )
154- . context ( "failed to parse registry server URL" ) ?
155- } ;
156-
157- match url. scheme ( ) {
158- "https" => { }
159- "http" => {
160- // Only allow HTTP connections to loopback
161- match url
162- . host ( )
163- . ok_or_else ( || anyhow ! ( "expected a host for URL `{url}`" ) ) ?
164- {
165- Host :: Domain ( d) => {
166- if d != "localhost" {
167- bail ! ( "an unsecured connection is not permitted to `{d}`" ) ;
168- }
169- }
170- Host :: Ipv4 ( ip) => {
171- if !ip. is_loopback ( ) {
172- bail ! ( "an unsecured connection is not permitted to address `{ip}`" ) ;
173- }
174- }
175- Host :: Ipv6 ( ip) => {
176- if !ip. is_loopback ( ) {
177- bail ! ( "an unsecured connection is not permitted to address `{ip}`" ) ;
178- }
179- }
180- }
181- }
182- _ => bail ! ( "expected a HTTPS scheme for URL `{url}`" ) ,
183- }
184- Ok ( url)
141+ pub fn url ( & self ) -> & RegistryUrl {
142+ & self . url
185143 }
186144
187145 /// Gets the latest checkpoint from the registry.
188146 pub async fn latest_checkpoint ( & self ) -> Result < SerdeEnvelope < MapCheckpoint > , ClientError > {
189- let url = self . url . join ( paths:: fetch_checkpoint ( ) ) . unwrap ( ) ;
147+ let url = self . url . join ( paths:: fetch_checkpoint ( ) ) ;
190148 tracing:: debug!( "getting latest checkpoint at `{url}`" ) ;
191149 into_result :: < _ , FetchError > ( reqwest:: get ( url) . await ?) . await
192150 }
@@ -196,7 +154,7 @@ impl Client {
196154 & self ,
197155 request : FetchLogsRequest < ' _ > ,
198156 ) -> Result < FetchLogsResponse , ClientError > {
199- let url = self . url . join ( paths:: fetch_logs ( ) ) . unwrap ( ) ;
157+ let url = self . url . join ( paths:: fetch_logs ( ) ) ;
200158 tracing:: debug!( "fetching logs at `{url}`" ) ;
201159
202160 let response = self . client . post ( url) . json ( & request) . send ( ) . await ?;
@@ -209,10 +167,7 @@ impl Client {
209167 log_id : & LogId ,
210168 request : PublishRecordRequest < ' _ > ,
211169 ) -> Result < PackageRecord , ClientError > {
212- let url = self
213- . url
214- . join ( & paths:: publish_package_record ( log_id) )
215- . unwrap ( ) ;
170+ let url = self . url . join ( & paths:: publish_package_record ( log_id) ) ;
216171 tracing:: debug!(
217172 "appending record to package `{id}` at `{url}`" ,
218173 id = request. id
@@ -228,10 +183,7 @@ impl Client {
228183 log_id : & LogId ,
229184 record_id : & RecordId ,
230185 ) -> Result < PackageRecord , ClientError > {
231- let url = self
232- . url
233- . join ( & paths:: package_record ( log_id, record_id) )
234- . unwrap ( ) ;
186+ let url = self . url . join ( & paths:: package_record ( log_id, record_id) ) ;
235187 tracing:: debug!( "getting record `{record_id}` for package `{log_id}` at `{url}`" ) ;
236188
237189 let response = reqwest:: get ( url) . await ?;
@@ -283,7 +235,7 @@ impl Client {
283235
284236 /// Proves the inclusion of the given package log heads in the registry.
285237 pub async fn prove_inclusion ( & self , request : InclusionRequest < ' _ > ) -> Result < ( ) , ClientError > {
286- let url = self . url . join ( paths:: prove_inclusion ( ) ) . unwrap ( ) ;
238+ let url = self . url . join ( paths:: prove_inclusion ( ) ) ;
287239 tracing:: debug!( "proving checkpoint inclusion at `{url}`" ) ;
288240
289241 let response = into_result :: < InclusionResponse , ProofError > (
@@ -303,7 +255,7 @@ impl Client {
303255 & self ,
304256 request : ConsistencyRequest < ' _ > ,
305257 ) -> Result < ( ) , ClientError > {
306- let url = self . url . join ( paths:: prove_consistency ( ) ) . unwrap ( ) ;
258+ let url = self . url . join ( paths:: prove_consistency ( ) ) ;
307259 let response = into_result :: < ConsistencyResponse , ProofError > (
308260 self . client . post ( url) . json ( & request) . send ( ) . await ?,
309261 )
0 commit comments