private void initHttpEngine() throws IOException { if (httpEngineFailure != null) { throw httpEngineFailure; } else if (httpEngine != null) { return; } connected = true; try { if (doOutput) { if (method.equals("GET")) { // they are requesting a stream to write to. This implies a POST method method = "POST"; } else if (!HttpMethod.hasRequestBody(method)) { // If the request method is neither POST nor PUT nor PATCH, then you're not writing throw new ProtocolException(method + " does not support writing"); } } // If the user set content length to zero, we know there will not be a request body. RetryableSink requestBody = doOutput && fixedContentLength == 0 ? Util.emptySink() : null; httpEngine = newHttpEngine(method, null, requestBody, null); } catch (IOException e) { httpEngineFailure = e; throw e; } }
private HttpEngine newHttpEngine(String method, StreamAllocation streamAllocation, RetryableSink requestBody, Response priorResponse) throws MalformedURLException, UnknownHostException { Request.Builder builder = new Request.Builder().url(Internal.instance.getHttpUrlChecked (getURL().toString())).method(method, HttpMethod.requiresRequestBody(method) ? EMPTY_REQUEST_BODY : null); Headers headers = this.requestHeaders.build(); int size = headers.size(); for (int i = 0; i < size; i++) { builder.addHeader(headers.name(i), headers.value(i)); } boolean bufferRequestBody = false; if (HttpMethod.permitsRequestBody(method)) { if (this.fixedContentLength != -1) { builder.header("Content-Length", Long.toString(this.fixedContentLength)); } else if (this.chunkLength > 0) { builder.header("Transfer-Encoding", "chunked"); } else { bufferRequestBody = true; } if (headers.get("Content-Type") == null) { builder.header("Content-Type", Client.FormMime); } } if (headers.get(Network.USER_AGENT) == null) { builder.header(Network.USER_AGENT, defaultUserAgent()); } Request request = builder.build(); OkHttpClient engineClient = this.client; if (!(Internal.instance.internalCache(engineClient) == null || getUseCaches())) { engineClient = this.client.clone().setCache(null); } return new HttpEngine(engineClient, request, bufferRequestBody, true, false, streamAllocation, requestBody, priorResponse); }
private HttpEngine getResponse() throws IOException { initHttpEngine(); if (this.httpEngine.hasResponse()) { return this.httpEngine; } while (true) { if (execute(true)) { Response response = this.httpEngine.getResponse(); Request followUp = this.httpEngine.followUpRequest(); if (followUp == null) { this.httpEngine.releaseStreamAllocation(); return this.httpEngine; } int i = this.followUpCount + 1; this.followUpCount = i; if (i > 20) { throw new ProtocolException("Too many follow-up requests: " + this .followUpCount); } this.url = followUp.url(); this.requestHeaders = followUp.headers().newBuilder(); Sink requestBody = this.httpEngine.getRequestBody(); if (!followUp.method().equals(this.method)) { requestBody = null; } if (requestBody == null || (requestBody instanceof RetryableSink)) { StreamAllocation streamAllocation = this.httpEngine.close(); if (!this.httpEngine.sameConnection(followUp.httpUrl())) { streamAllocation.release(); streamAllocation = null; } this.httpEngine = newHttpEngine(followUp.method(), streamAllocation, (RetryableSink) requestBody, response); } else { throw new HttpRetryException("Cannot retry streamed HTTP body", this .responseCode); } } } }
/** * Performs the request and returns the response. May return null if this * call was canceled. */ private Response getResponse() throws IOException { // Copy body metadata to the appropriate request headers. RequestBody body = request.body(); RetryableSink requestBodyOut = null; if (body != null) { Request.Builder requestBuilder = request.newBuilder(); MediaType contentType = body.contentType(); if (contentType != null) { requestBuilder.header("Content-Type", contentType.toString()); } long contentLength = body.contentLength(); if (contentLength != -1) { requestBuilder.header("Content-Length", Long.toString(contentLength)); requestBuilder.removeHeader("Transfer-Encoding"); } else { requestBuilder.header("Transfer-Encoding", "chunked"); requestBuilder.removeHeader("Content-Length"); } request = requestBuilder.build(); } else if (HttpMethod.hasRequestBody(request.method())) { requestBodyOut = Util.emptySink(); } // Create the initial HTTP engine. Retries and redirects need new engine for each attempt. engine = new HttpEngine(client, request, false, null, null, requestBodyOut, null); while (true) { if (canceled) return null; try { engine.sendRequest(); if (request.body() != null) { BufferedSink sink = engine.getBufferedRequestBody(); request.body().writeTo(sink); } engine.readResponse(); } catch (IOException e) { HttpEngine retryEngine = engine.recover(e, null); if (retryEngine != null) { // sync up the rquest request = engine.getRequest(); engine = retryEngine; continue; } // Give up; recovery is not possible. throw e; } Response response = engine.getResponse(); Request followUp = engine.followUpRequest(); if (followUp == null) { engine.releaseConnection(); return response.newBuilder() .body(new RealResponseBody(response, engine.getResponseBody())) .build(); } if (engine.getResponse().isRedirect() && ++redirectionCount > MAX_REDIRECTS) { throw new ProtocolException("Too many redirects: " + redirectionCount); } if (!engine.sameConnection(followUp.url())) { engine.releaseConnection(); } Connection connection = engine.close(); request = followUp; engine = new HttpEngine(client, request, false, connection, null, null, response); } }
public static RetryableSink emptySink() { return EMPTY_SINK; }
private HttpEngine newHttpEngine(String method, Connection connection, RetryableSink requestBody, Response priorResponse) { Request.Builder builder = new Request.Builder() .url(getURL()) .method(method, null /* No body; that's passed separately. */); Headers headers = requestHeaders.build(); for (int i = 0; i < headers.size(); i++) { builder.addHeader(headers.name(i), headers.value(i)); } boolean bufferRequestBody = false; if (HttpMethod.hasRequestBody(method)) { // Specify how the request body is terminated. if (fixedContentLength != -1) { builder.header("Content-Length", Long.toString(fixedContentLength)); } else if (chunkLength > 0) { builder.header("Transfer-Encoding", "chunked"); } else { bufferRequestBody = true; } // Add a content type for the request body, if one isn't already present. if (headers.get("Content-Type") == null) { builder.header("Content-Type", "application/x-www-form-urlencoded"); } } if (headers.get("User-Agent") == null) { builder.header("User-Agent", defaultUserAgent()); } Request request = builder.build(); // If we're currently not using caches, make sure the engine's client doesn't have one. OkHttpClient engineClient = client; if (Internal.instance.internalCache(engineClient) != null && !getUseCaches()) { engineClient = client.clone().setCache(null); } request = client.preConfigRequest(request); return new HttpEngine(engineClient, request, bufferRequestBody, connection, null, requestBody, priorResponse); }
/** * 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; } Response response = httpEngine.getResponse(); Request followUp = httpEngine.followUpRequest(); if (followUp == null) { httpEngine.releaseConnection(); return httpEngine; } if (response.isRedirect() && ++redirectionCount > HttpEngine.MAX_REDIRECTS) { throw new ProtocolException("Too many redirects: " + redirectionCount); } // The first request was insufficient. Prepare for another... url = followUp.url(); requestHeaders = followUp.headers().newBuilder(); // 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. Sink requestBody = httpEngine.getRequestBody(); if (!followUp.method().equals(method)) { requestBody = null; } if (requestBody != null && !(requestBody instanceof RetryableSink)) { throw new HttpRetryException("Cannot retry streamed HTTP body", responseCode); } if (!httpEngine.sameConnection(followUp.url())) { httpEngine.releaseConnection(); } Connection connection = httpEngine.close(); httpEngine = newHttpEngine(followUp.method(), connection, (RetryableSink) requestBody, response); } }
private HttpEngine newHttpEngine(String method, Connection connection, RetryableSink requestBody, Response priorResponse) { // OkHttp's Call API requires a placeholder body; the real body will be streamed separately. RequestBody placeholderBody = HttpMethod.requiresRequestBody(method) ? EMPTY_REQUEST_BODY : null; Request.Builder builder = new Request.Builder() .url(getURL()) .method(method, placeholderBody); Headers headers = requestHeaders.build(); for (int i = 0, size = headers.size(); i < size; i++) { builder.addHeader(headers.name(i), headers.value(i)); } boolean bufferRequestBody = false; if (HttpMethod.permitsRequestBody(method)) { // Specify how the request body is terminated. if (fixedContentLength != -1) { builder.header("Content-Length", Long.toString(fixedContentLength)); } else if (chunkLength > 0) { builder.header("Transfer-Encoding", "chunked"); } else { bufferRequestBody = true; } // Add a content type for the request body, if one isn't already present. if (headers.get("Content-Type") == null) { builder.header("Content-Type", "application/x-www-form-urlencoded"); } } if (headers.get("User-Agent") == null) { builder.header("User-Agent", defaultUserAgent()); } Request request = builder.build(); // If we're currently not using caches, make sure the engine's client doesn't have one. OkHttpClient engineClient = client; if (Internal.instance.internalCache(engineClient) != null && !getUseCaches()) { engineClient = client.clone().setCache(null); } return new HttpEngine(engineClient, request, bufferRequestBody, true, false, connection, null, requestBody, priorResponse); }
/** * 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; } Response response = httpEngine.getResponse(); Request followUp = httpEngine.followUpRequest(); if (followUp == null) { httpEngine.releaseConnection(); return httpEngine; } if (++followUpCount > HttpEngine.MAX_FOLLOW_UPS) { throw new ProtocolException("Too many follow-up requests: " + followUpCount); } // The first request was insufficient. Prepare for another... url = followUp.url(); requestHeaders = followUp.headers().newBuilder(); // 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. Sink requestBody = httpEngine.getRequestBody(); if (!followUp.method().equals(method)) { requestBody = null; } if (requestBody != null && !(requestBody instanceof RetryableSink)) { throw new HttpRetryException("Cannot retry streamed HTTP body", responseCode); } if (!httpEngine.sameConnection(followUp.url())) { httpEngine.releaseConnection(); } Connection connection = httpEngine.close(); httpEngine = newHttpEngine(followUp.method(), connection, (RetryableSink) requestBody, response); } }