HttpClient redirecting to URL with spaces throwing exception

a 夏天 提交于 2021-02-17 21:30:45

问题


I am accessing a URL that's redirecting me to a URL with spaces in it. (Using HttpClient 4.x) How do I prevent this from throwing an error (replacing the spaces with %20 not +)

08-06 02:45:56.486: WARN/System.err(655): org.apache.http.client.ClientProtocolException
08-06 02:45:56.493: WARN/System.err(655):     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:557)
08-06 02:45:56.534: WARN/System.err(655):     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:509)
08-06 02:45:56.603: WARN/System.err(655):     at com.romcessed.romsearch.searchproviders.DopeRomsConnector$DownloadROMTask.doInBackground(DopeRomsConnector.java:636)
08-06 02:45:56.623: WARN/System.err(655):     at com.romcessed.romsearch.searchproviders.DopeRomsConnector$DownloadROMTask.doInBackground(DopeRomsConnector.java:1)
08-06 02:45:56.643: WARN/System.err(655):     at android.os.AsyncTask$2.call(AsyncTask.java:185)
08-06 02:45:56.663: WARN/System.err(655):     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
08-06 02:45:56.683: WARN/System.err(655):     at java.util.concurrent.FutureTask.run(FutureTask.java:137)
08-06 02:45:56.693: WARN/System.err(655):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
08-06 02:45:56.713: WARN/System.err(655):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
08-06 02:45:56.713: WARN/System.err(655):     at java.lang.Thread.run(Thread.java:1096)
08-06 02:45:56.743: WARN/System.err(655): Caused by: org.apache.http.ProtocolException: Invalid redirect URI: http://somewebsite.com/some file with spaces.zip
08-06 02:45:56.787: WARN/System.err(655):     at org.apache.http.impl.client.DefaultRedirectHandler.getLocationURI(DefaultRedirectHandler.java:116)
08-06 02:45:56.803: WARN/System.err(655):     at org.apache.http.impl.client.DefaultRequestDirector.handleResponse(DefaultRequestDirector.java:892)
08-06 02:45:56.813: WARN/System.err(655):     at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:457)
08-06 02:45:56.843: WARN/System.err(655):     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
08-06 02:45:56.843: WARN/System.err(655):     ... 9 more
08-06 02:45:56.873: WARN/System.err(655): Caused by: java.net.URISyntaxException: Illegal character in path at index #: http://somewebsite.com/some file with spaces.zip
08-06 02:45:56.913: WARN/System.err(655):     at java.net.URI$Helper.validatePath(URI.java:448)
08-06 02:45:56.923: WARN/System.err(655):     at java.net.URI$Helper.parseURI(URI.java:398)
08-06 02:45:56.953: WARN/System.err(655):     at java.net.URI$Helper.access$100(URI.java:302)
08-06 02:45:56.963: WARN/System.err(655):     at java.net.URI.<init>(URI.java:87)
08-06 02:45:56.993: WARN/System.err(655):     at org.apache.http.impl.client.DefaultRedirectHandler.getLocationURI(DefaultRedirectHandler.java:114)
08-06 02:45:57.013: WARN/System.err(655):     ... 12 more

回答1:


The Apache HTTP library allows you to register a RedirectHandler object that will get invoked whenever a redirect occurs. You can use this to intercept the redirect and fix it.

(That being said, the site that's sending you this redirect is broken. You should contact them and let them know.)

class CustomRedirectHandler extends DefaultRedirectHandler {
    public URI getLocationURI(HttpResponse response, HttpContext context) {
        // Extract the Location: header and manually convert spaces to %20's
        // Return the corrected URI
    }
}

DefaultHttpClient httpClient = new DefaultHttpClient();
RedirectHandler customRedirectHandler = new CustomRedirectHandler();
//...
httpClient.setRedirectHandler(customRedirectHandler);



回答2:


Here is my working code :)

class spaceRedirectHandler extends DefaultRedirectHandler{

                private static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";

                public spaceRedirectHandler() {
                    super();
                }

                public boolean isRedirectRequested(
                        final HttpResponse response,
                        final HttpContext context) {
                    if (response == null) {
                        throw new IllegalArgumentException("HTTP response may not be null");
                    }
                    int statusCode = response.getStatusLine().getStatusCode();
                    switch (statusCode) {
                    case HttpStatus.SC_MOVED_TEMPORARILY:
                    case HttpStatus.SC_MOVED_PERMANENTLY:
                    case HttpStatus.SC_SEE_OTHER:
                    case HttpStatus.SC_TEMPORARY_REDIRECT:
                        return true;
                    default:
                        return false;
                    } //end of switch
                }

                public URI getLocationURI(
                        final HttpResponse response, 
                        final HttpContext context) throws ProtocolException {
                    if (response == null) {
                        throw new IllegalArgumentException("HTTP response may not be null");
                    }
                    //get the location header to find out where to redirect to
                    Header locationHeader = response.getFirstHeader("location");
                    if (locationHeader == null) {
                        // got a redirect response, but no location header
                        throw new ProtocolException(
                                "Received redirect response " + response.getStatusLine()
                                + " but no location header");
                    }
//HERE IS THE MODIFIED LINE OF CODE
                    String location = locationHeader.getValue().replaceAll (" ", "%20");

                    URI uri;
                    try {
                        uri = new URI(location);            
                    } catch (URISyntaxException ex) {
                        throw new ProtocolException("Invalid redirect URI: " + location, ex);
                    }

                    HttpParams params = response.getParams();
                    // rfc2616 demands the location value be a complete URI
                    // Location       = "Location" ":" absoluteURI
                    if (!uri.isAbsolute()) {
                        if (params.isParameterTrue(ClientPNames.REJECT_RELATIVE_REDIRECT)) {
                            throw new ProtocolException("Relative redirect location '" 
                                    + uri + "' not allowed");
                        }
                        // Adjust location URI
                        HttpHost target = (HttpHost) context.getAttribute(
                                ExecutionContext.HTTP_TARGET_HOST);
                        if (target == null) {
                            throw new IllegalStateException("Target host not available " +
                                    "in the HTTP context");
                        }

                        HttpRequest request = (HttpRequest) context.getAttribute(
                                ExecutionContext.HTTP_REQUEST);

                        try {
                            URI requestURI = new URI(request.getRequestLine().getUri());
                            URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, true);
                            uri = URIUtils.resolve(absoluteRequestURI, uri); 
                        } catch (URISyntaxException ex) {
                            throw new ProtocolException(ex.getMessage(), ex);
                        }
                    }

                    if (params.isParameterFalse(ClientPNames.ALLOW_CIRCULAR_REDIRECTS)) {

                        RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute(
                                REDIRECT_LOCATIONS);

                        if (redirectLocations == null) {
                            redirectLocations = new RedirectLocations();
                            context.setAttribute(REDIRECT_LOCATIONS, redirectLocations);
                        }

                        URI redirectURI;
                        if (uri.getFragment() != null) {
                            try {
                                HttpHost target = new HttpHost(
                                        uri.getHost(), 
                                        uri.getPort(),
                                        uri.getScheme());
                                redirectURI = URIUtils.rewriteURI(uri, target, true);
                            } catch (URISyntaxException ex) {
                                throw new ProtocolException(ex.getMessage(), ex);
                            }
                        } else {
                            redirectURI = uri;
                        }

                        if (redirectLocations.contains(redirectURI)) {
                            throw new CircularRedirectException("Circular redirect to '" +
                                    redirectURI + "'");
                        } else {
                            redirectLocations.add(redirectURI);
                        }
                    }

                    return uri;
                }
        }



回答3:


I recommend creating custom redirect strategy

class CustomRedirectStrategy extends DefaultRedirectStrategy {
  // NOTE: Hack for bad redirects such as: http://www.healio.com/Rss/Allergy%20Immunology
  override def createLocationURI(location: String): URI = {
    try {
      super.createLocationURI(location)
    } catch {
      case ex: ProtocolException =>
        val url = new URL(location)
        val uri = new URI(url.getProtocol, url.getUserInfo, url.getHost, url.getPort, url.getPath, url.getQuery, url.getRef)
        uri
    }
  }
}

that you can set in your client via setRedirectStrategy method.

HttpAsyncClients.custom.setRedirectStrategy(new CustomRedirectStrategy).build



回答4:


Another working code example that will replace spaces with %20, based on https://stackoverflow.com/a/8962879/956415

private download(){

    ...

    mHttpClient = new DefaultHttpClient(httpParams);

    mHttpClient.setRedirectHandler(new DefaultRedirectHandler() {
        @Override
        public boolean isRedirectRequested(HttpResponse httpResponse, HttpContext httpContext) {
            return super.isRedirectRequested(httpResponse, httpContext);
        }

        @Override
        public URI getLocationURI(HttpResponse httpResponse, HttpContext httpContext) throws ProtocolException {
            return sanitizeUrl(httpResponse.getFirstHeader("location").getValue());
        }
    });


}

private URI sanitizeUrl(String sanitizeURL) throws ProtocolException {

    URI uri = null;

    try {
        URL url = new URL(URLDecoder.decode(sanitizeURL, UTF_8));
        // https://stackoverflow.com/a/8962879/956415
        uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
    } catch (URISyntaxException | MalformedURLException | UnsupportedEncodingException e) {
        throw new ProtocolException(e.getMessage(), e);
    }

    return uri;
}



回答5:


If anyone is looking for a complete solution in 2020 and beyond, see below. Extend the DefaultRedirectStrategy and override the method that creates the redirect URI to transform spaces into %20 sequences.

public class CleanUrlRedirectStrategy extends DefaultRedirectStrategy
{
    private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );

    // Clean spaces in redirect URLs to prevent an IOException
    @Override
    protected URI createLocationURI( final @NotNull String orig ) throws ProtocolException
    {
        // Replace spaces
        Objects.requireNonNull( orig );
        var fixed = orig.replaceAll("\\s","%20"); // Not \\s+
        if ( ! orig.equals( fixed ) )
        {
            logger.debug( "Cleaned redirect URL from '{}' to '{}'", orig, fixed );
        }

        return super.createLocationURI( fixed );
    }
}

and

var httpClient = HttpClientBuilder
    .create()
    .setRedirectStrategy( new CleanUrlRedirectStrategy() )
    .build();



回答6:


http://download.oracle.com/javase/6/docs/api/java/net/URI.html

or just replace the "+" with the %20

similar question

In a URL, should spaces be encoded using %20 or +?



来源:https://stackoverflow.com/questions/3420767/httpclient-redirecting-to-url-with-spaces-throwing-exception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!