/** * Report and attempt to recover from {@code e}. Returns true if the HTTP engine was replaced and * the request should be retried. Otherwise the failure is permanent. */ private boolean handleFailure(IOException e) throws IOException { RouteSelector routeSelector = httpEngine.routeSelector; if (routeSelector != null && httpEngine.connection != null) { routeSelector.connectFailed(httpEngine.connection, e); } OutputStream requestBody = httpEngine.getRequestBody(); boolean canRetryRequestBody = requestBody == null || requestBody instanceof RetryableOutputStream || (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable()); if (routeSelector == null && httpEngine.connection == null // No connection. || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt. || !isRecoverable(e) || !canRetryRequestBody) { httpEngineFailure = e; return false; } httpEngine.release(true); RetryableOutputStream retryableOutputStream = requestBody instanceof RetryableOutputStream ? (RetryableOutputStream) requestBody : null; httpEngine = newHttpEngine(method, rawRequestHeaders, null, retryableOutputStream); httpEngine.routeSelector = routeSelector; // Keep the same routeSelector. if (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable()) { httpEngine.sendRequest(); faultRecoveringRequestBody.replaceStream(httpEngine.getRequestBody()); } return true; }
@Override public final OutputStream getOutputStream() throws IOException { connect(); OutputStream out = httpEngine.getRequestBody(); if (out == null) { throw new ProtocolException("method does not support a request body: " + method); } else if (httpEngine.hasResponse()) { throw new ProtocolException("cannot write request body after response has been read"); } if (faultRecoveringRequestBody == null) { faultRecoveringRequestBody = new FaultRecoveringOutputStream(MAX_REPLAY_BUFFER_LENGTH, out) { @Override protected OutputStream replacementStream(IOException e) throws IOException { if (httpEngine.getRequestBody() instanceof AbstractOutputStream && ((AbstractOutputStream) httpEngine.getRequestBody()).isClosed()) { return null; // Don't recover once the underlying stream has been closed. } if (handleFailure(e)) { return httpEngine.getRequestBody(); } return null; // This is a permanent failure. } }; } return faultRecoveringRequestBody; }
/** * Aggressively tries to get the final HTTP response, potentially making many HTTP requests in the * process in order to cope with redirects and authentication. */ private HttpEngine getResponse() throws IOException { initHttpEngine(); if (httpEngine.hasResponse()) { return httpEngine; } while (true) { if (!execute(true)) { continue; } Retry retry = processResponseHeaders(); if (retry == Retry.NONE) { httpEngine.automaticallyReleaseConnectionToPool(); return httpEngine; } // The first request was insufficient. Prepare for another... String retryMethod = method; OutputStream requestBody = httpEngine.getRequestBody(); // Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM // redirect should keep the same method, Chrome, Firefox and the // RI all issue GETs when following any redirect. int responseCode = getResponseCode(); if (responseCode == HTTP_MULT_CHOICE || responseCode == HTTP_MOVED_PERM || responseCode == HTTP_MOVED_TEMP || responseCode == HTTP_SEE_OTHER) { retryMethod = "GET"; requestBody = null; } if (requestBody != null && !(requestBody instanceof RetryableOutputStream)) { throw new HttpRetryException( "Cannot retry streamed HTTP body", httpEngine.getResponseCode()); } if (retry == Retry.DIFFERENT_CONNECTION) { httpEngine.automaticallyReleaseConnectionToPool(); } httpEngine.release(false); httpEngine = newHttpEngine( retryMethod, rawRequestHeaders, httpEngine.getConnection(), (RetryableOutputStream) requestBody); } }
@Override public final OutputStream getOutputStream() throws IOException { connect(); OutputStream out = httpEngine.getRequestBody(); if (out == null) { throw new ProtocolException("method does not support a request body: " + method); } else if (httpEngine.hasResponse()) { throw new ProtocolException("cannot write request body after response has been read"); } return out; }