2121import kotlin .Pair ;
2222import org .commonjava .util .sidecar .config .ProxyConfiguration ;
2323import org .commonjava .util .sidecar .interceptor .ExceptionHandler ;
24+ import org .commonjava .util .sidecar .model .dto .HistoricalEntryDTO ;
2425import org .commonjava .util .sidecar .util .OtelAdapter ;
2526import org .commonjava .util .sidecar .util .ProxyStreamingOutput ;
2627import org .commonjava .util .sidecar .util .UrlUtils ;
3132import javax .enterprise .context .ApplicationScoped ;
3233import javax .inject .Inject ;
3334import javax .ws .rs .core .Response ;
35+ import java .io .ByteArrayOutputStream ;
36+ import java .io .IOException ;
3437import java .io .InputStream ;
38+ import java .util .LinkedHashMap ;
39+ import java .util .Map ;
3540
3641import static io .vertx .core .http .HttpMethod .HEAD ;
3742import static javax .ws .rs .core .Response .Status .INTERNAL_SERVER_ERROR ;
@@ -54,28 +59,34 @@ public class ProxyService
5459 @ Inject
5560 OtelAdapter otel ;
5661
62+ @ Inject
63+ ReportService reportService ;
64+
5765 public Uni <Response > doHead ( String trackingId , String packageType , String type , String name , String path ,
58- HttpServerRequest request ) throws Exception
66+ HttpServerRequest request )
67+ throws Exception
5968 {
6069 String contentPath = UrlUtils .buildUrl ( FOLO_TRACK_REST_BASE_PATH , trackingId , packageType , type , name , path );
6170 return doHead ( contentPath , request );
6271 }
6372
64- public Uni <Response > doHead ( String path , HttpServerRequest request ) throws Exception
73+ public Uni <Response > doHead ( String path , HttpServerRequest request )
74+ throws Exception
6575 {
6676 return normalizePathAnd ( path , p -> classifier .classifyAnd ( p , request , ( client , service ) -> wrapAsyncCall (
67- client .head ( p , request ).call (), request .method () ) ) );
77+ client .head ( p , request ).call (), request .method () ) ) );
6878 }
6979
7080 public Uni <Response > doGet ( String trackingId , String packageType , String type , String name , String path ,
71- HttpServerRequest request ) throws Exception
81+ HttpServerRequest request )
82+ throws Exception
7283 {
7384 String contentPath = UrlUtils .buildUrl ( FOLO_TRACK_REST_BASE_PATH , trackingId , packageType , type , name , path );
7485 return doGet ( contentPath , request );
7586 }
7687
7788 public Uni <Response > doGet ( String path , HttpServerRequest request )
78- throws Exception
89+ throws Exception
7990 {
8091 return normalizePathAnd ( path , p -> classifier .classifyAnd ( p , request , ( client , service ) -> wrapAsyncCall (
8192 client .get ( p , request ).call (), request .method () ) ) );
@@ -113,16 +124,87 @@ public Uni<Response> doPut( String path, InputStream is, HttpServerRequest reque
113124 public Uni <Response > doDelete ( String path , HttpServerRequest request ) throws Exception
114125 {
115126 return normalizePathAnd ( path , p -> classifier .classifyAnd ( p , request , ( client , service ) -> wrapAsyncCall (
116- client .delete ( p ).headersFrom ( request ).call (), request .method () ) ) );
127+ client .delete ( p ).headersFrom ( request ).call (), request .method () ) ) );
117128 }
118129
119130 public Uni <Response > wrapAsyncCall ( WebClientAdapter .CallAdapter asyncCall , HttpMethod method )
120131 {
121- Uni <Response > ret =
122- asyncCall .enqueue ().onItem ().transform ( ( resp ) -> convertProxyResp ( resp , method ) );
132+ Uni <Response > ret = asyncCall .enqueue ().onItem ().transform ( ( resp ) -> convertProxyResp ( resp , method ) );
123133 return ret .onFailure ().recoverWithItem ( this ::handleProxyException );
124134 }
125135
136+ public Uni <Boolean > validateChecksum ( String trackingId , String packageType , String type , String name , String path ,
137+ HttpServerRequest request )
138+ {
139+ Map <String , String > localChecksums = getChecksums ( path );
140+ Uni <Boolean > resultUni = Uni .createFrom ().item ( false );
141+
142+ for ( String checksumType : localChecksums .keySet () )
143+ {
144+ String localChecksum = localChecksums .get ( checksumType );
145+ if ( localChecksum == null )
146+ {
147+ continue ;
148+ }
149+ String checksumUrl = path + "." + checksumType ;
150+ resultUni = resultUni .onItem ().call ( () -> {
151+ try
152+ {
153+ return downloadAndCompareChecksum ( trackingId , packageType , type , name , checksumUrl , localChecksum ,
154+ request ).onItem ().invoke ( result -> {
155+ if ( result != null && result )
156+ {
157+ // This is just used to skip loop to avoid unnecessary checksum download
158+ logger .debug (
159+ "Found the valid checksum compare result, stopping further checks, remote path {}" ,
160+ checksumUrl );
161+ throw new FoundValidChecksumException ();
162+ }
163+ } );
164+ }
165+ catch ( Exception e )
166+ {
167+ logger .error ( "Checksum download compare error for path: {}" , checksumUrl , e );
168+ }
169+ return null ;
170+ } );
171+ }
172+ return resultUni .onFailure ().recoverWithItem ( false ).onItem ().transform ( result -> {
173+ // If catch FoundValidChecksumException,return true
174+ return true ;
175+ } ); // If no valid checksum compare result found, return false
176+ }
177+
178+ private Uni <Boolean > downloadAndCompareChecksum ( String trackingId , String packageType , String type , String name ,
179+ String checksumUrl , String localChecksum ,
180+ HttpServerRequest request )
181+ throws Exception
182+ {
183+ return doGet ( trackingId , packageType , type , name , checksumUrl , request ).onItem ().transform ( response -> {
184+ if ( response .getStatus () == Response .Status .OK .getStatusCode () )
185+ {
186+ ProxyStreamingOutput streamingOutput = (ProxyStreamingOutput ) response .getEntity ();
187+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream ())
188+ {
189+ streamingOutput .write ( outputStream );
190+ String remoteChecksum = outputStream .toString ();
191+ return localChecksum .equals ( remoteChecksum );
192+ }
193+ catch ( IOException e )
194+ {
195+ logger .error ( "Error to read remote checksum, path:{}." , checksumUrl , e );
196+ return null ;
197+ }
198+ }
199+ else
200+ {
201+ logger .error ( "Failed to download remote checksum for {}: HTTP {}." , checksumUrl ,
202+ response .getStatus () );
203+ return null ;
204+ }
205+ } );
206+ }
207+
126208 /**
127209 * Send status 500 with error message body.
128210 * @param t error
@@ -162,4 +244,46 @@ private boolean isHeaderAllowed( Pair<? extends String, ? extends String> header
162244 String key = header .getFirst ();
163245 return !FORBIDDEN_HEADERS .contains ( key .toLowerCase () );
164246 }
247+
248+ private Map <String , String > getChecksums ( String path )
249+ {
250+ Map <String , String > result = new LinkedHashMap <>();
251+ HistoricalEntryDTO entryDTO = reportService .getHistoricalContentMap ().get ( path );
252+ if ( entryDTO != null )
253+ {
254+ result .put ( ChecksumType .SHA1 .getValue (), entryDTO .getSha1 () );
255+ result .put ( ChecksumType .SHA256 .getValue (), entryDTO .getSha256 () );
256+ result .put ( ChecksumType .MD5 .getValue (), entryDTO .getMd5 () );
257+ }
258+
259+ return result ;
260+ }
261+
262+ enum ChecksumType
263+ {
264+ SHA1 ( "sha1" ),
265+ SHA256 ( "sha256" ),
266+ MD5 ( "md5" );
267+
268+ private final String value ;
269+
270+ ChecksumType ( String value )
271+ {
272+ this .value = value ;
273+ }
274+
275+ public String getValue ()
276+ {
277+ return value ;
278+ }
279+ }
280+
281+ class FoundValidChecksumException
282+ extends RuntimeException
283+ {
284+ public FoundValidChecksumException ()
285+ {
286+ super ( "Found a valid checksum, stopping further checks." );
287+ }
288+ }
165289}
0 commit comments