Skip to content

Commit 938dfc7

Browse files
authored
Merge pull request #1465 from lesserwhirls/gh-1435-log-403
Do not stop trying to disambiguate http locations when 403s are encountered
2 parents fbfe6c3 + 780aa1c commit 938dfc7

2 files changed

Lines changed: 58 additions & 21 deletions

File tree

cdm/core/src/main/java/ucar/nc2/dataset/DatasetUrl.java

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
/*
2-
* Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
2+
* Copyright (c) 1998-2025 John Caron and University Corporation for Atmospheric Research/Unidata
33
* See LICENSE for license information.
44
*/
5+
56
package ucar.nc2.dataset;
67

78
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
89
import static java.net.HttpURLConnection.HTTP_NOT_ACCEPTABLE;
910
import static java.net.HttpURLConnection.HTTP_OK;
1011
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
12+
1113
import com.google.common.annotations.VisibleForTesting;
14+
import java.io.BufferedInputStream;
15+
import java.io.File;
16+
import java.io.FileInputStream;
17+
import java.io.IOException;
18+
import java.util.ArrayList;
19+
import java.util.HashMap;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.Objects;
23+
import java.util.Optional;
1224
import javax.annotation.Nullable;
1325

14-
import com.google.common.collect.Multimap;
1526
import thredds.client.catalog.ServiceType;
16-
import thredds.inventory.MFile;
17-
import thredds.inventory.MFiles;
1827
import ucar.httpservices.HTTPFactory;
1928
import ucar.httpservices.HTTPMethod;
2029
import ucar.nc2.util.EscapeStrings;
2130
import ucar.unidata.util.StringUtil2;
2231
import ucar.unidata.util.Urlencoded;
23-
import java.io.*;
24-
import java.util.*;
2532

2633
/**
2734
* Detection of the protocol from a location string.
@@ -31,6 +38,7 @@
3138
* @since 10/20/2015.
3239
*/
3340
public class DatasetUrl {
41+
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DatasetUrl.class);
3442
private static final String alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
3543
private static final String slashalpha = "\\/" + alpha;
3644

@@ -398,15 +406,23 @@ private static ServiceType disambiguateHttp(String location) throws IOException
398406
return null;
399407
}
400408

409+
private static void handleCheckIfResponse(HTTPMethod method, int statusCode, ServiceType serviceChecked)
410+
throws IOException {
411+
if (statusCode == HTTP_UNAUTHORIZED) {
412+
throw new IOException("Unauthorized to open dataset " + method.getURI());
413+
} else if (statusCode == HTTP_FORBIDDEN) {
414+
logger.warn(String.format(
415+
"Forbidden Request to %s - assuming remote server is not a %s server, but could also indicate incorrect credentials.",
416+
method.getURI(), serviceChecked));
417+
}
418+
}
419+
401420
// cdmremote
402421
private static ServiceType checkIfCdmr(String location) throws IOException {
403422
try (HTTPMethod method = HTTPFactory.Head(location + "?req=header")) {
404423
int statusCode = method.execute();
405424
if (statusCode >= 300) {
406-
if (statusCode == HTTP_UNAUTHORIZED || statusCode == HTTP_FORBIDDEN)
407-
throw new IOException("Unauthorized to open dataset " + location);
408-
else
409-
throw new IOException(location + " is not a valid URL, return status=" + statusCode);
425+
handleCheckIfResponse(method, statusCode, ServiceType.CdmRemote);
410426
}
411427

412428
Optional<String> value = method.getResponseHeaderValue("Content-Description");
@@ -442,8 +458,7 @@ private static ServiceType checkIfDods(String location) throws IOException {
442458
throw new IOException("OPeNDAP Server Error= " + method.getResponseAsString());
443459
}
444460
}
445-
if (status == HTTP_UNAUTHORIZED || status == HTTP_FORBIDDEN)
446-
throw new IOException("Unauthorized to open dataset " + location);
461+
handleCheckIfResponse(method, status, ServiceType.OPENDAP);
447462

448463
// not dods
449464
return null;
@@ -471,6 +486,7 @@ else if (location.endsWith(".html"))
471486
return ServiceType.DAP4;
472487
}
473488
}
489+
handleCheckIfResponse(method, status, ServiceType.DAP4);
474490
// not dap4
475491
return null;
476492
}
@@ -497,14 +513,15 @@ private static boolean checkIfRemoteNcml(String location) throws IOException {
497513
method.setRequestHeader("accept-encoding", "identity");
498514
int statusCode = method.execute();
499515
if (statusCode >= 300) {
500-
if (statusCode == HTTP_UNAUTHORIZED) {
501-
throw new IOException("Unauthorized to open dataset " + location);
502-
} else if (statusCode == HTTP_NOT_ACCEPTABLE) {
516+
handleCheckIfResponse(method, statusCode, ServiceType.NCML);
517+
// additional checks
518+
if (statusCode == HTTP_NOT_ACCEPTABLE) {
503519
String msg = location + " - this server does not support returning content without any encoding.";
504520
msg = msg + " Please download the file locally. Return status=" + statusCode;
505521
throw new IOException(msg);
506522
} else {
507-
throw new IOException(location + " is not a valid URL, return status=" + statusCode);
523+
throw new IOException(String.format("Error opening %s: %s, (HTTP Status Code %d)", location,
524+
method.getResponseAsString(), statusCode));
508525
}
509526
}
510527

cdm/core/src/main/java/ucar/unidata/io/http/HTTPRandomAccessFile.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/*
2-
* Copyright (c) 1998-2019 University Corporation for Atmospheric Research/Unidata
2+
* Copyright (c) 1998-2025 University Corporation for Atmospheric Research/Unidata
33
* See LICENSE for license information.
44
*/
5+
56
package ucar.unidata.io.http;
67

78
import java.io.FileNotFoundException;
@@ -40,6 +41,9 @@ public final class HTTPRandomAccessFile extends RemoteRandomAccessFile {
4041
private static final long httpMaxCacheSize = Long
4142
.parseLong(System.getProperty("ucar.unidata.io.http.maxReadCacheSize", String.valueOf(defaultMaxReadCacheSize)));
4243

44+
private static final String HTTPS = "https";
45+
private static final String HTTP = "http";
46+
4347
private static final boolean debug = false, debugDetails = false;
4448

4549
///////////////////////////////////////////////////////////////////////////////////
@@ -234,7 +238,7 @@ public long getLastModified() {
234238
*/
235239
public static class Provider implements RandomAccessFileProvider {
236240

237-
private static final List<String> possibleSchemes = Arrays.asList("http", "https", "nodods", "httpserver");
241+
private static final List<String> possibleSchemes = Arrays.asList(HTTP, HTTPS, "nodods", "httpserver");
238242

239243
@Override
240244
public boolean isOwnerOf(String location) {
@@ -250,10 +254,26 @@ public RandomAccessFile open(String location) throws IOException {
250254
@Override
251255
public RandomAccessFile open(String location, int bufferSize) throws IOException {
252256
String scheme = location.split(":")[0];
253-
if (!scheme.equalsIgnoreCase("https") && !scheme.equalsIgnoreCase("http")) {
254-
location = location.replace(scheme, "http");
257+
boolean fallback = false;
258+
if (!scheme.equalsIgnoreCase(HTTPS) && !scheme.equalsIgnoreCase(HTTP)) {
259+
location = location.replaceFirst(scheme, HTTPS);
260+
fallback = true;
261+
}
262+
HTTPRandomAccessFile raf;
263+
try {
264+
// try with https first
265+
raf = new HTTPRandomAccessFile(location, bufferSize, httpMaxCacheSize);
266+
} catch (IOException e) {
267+
if (fallback) {
268+
// if we had to guess the scheme, fallback to http, and if it does not work, let the IOError be thrown
269+
raf = new HTTPRandomAccessFile(location.replaceFirst(HTTPS, HTTP), bufferSize, httpMaxCacheSize);
270+
} else {
271+
// didn't try to fallback to http, just throw the original error
272+
throw e;
273+
}
255274
}
256-
return new HTTPRandomAccessFile(location, bufferSize, httpMaxCacheSize);
275+
276+
return raf;
257277
}
258278
}
259279
}

0 commit comments

Comments
 (0)