All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
okhttp3.internal.http2.Http2Codec Maven / Gradle / Ivy
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http2;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.Internal;
import okhttp3.internal.Util;
import okhttp3.internal.connection.StreamAllocation;
import okhttp3.internal.http.HttpCodec;
import okhttp3.internal.http.HttpHeaders;
import okhttp3.internal.http.RealResponseBody;
import okhttp3.internal.http.RequestLine;
import okhttp3.internal.http.StatusLine;
import okio.Buffer;
import okio.ByteString;
import okio.ForwardingSource;
import okio.Okio;
import okio.Sink;
import okio.Source;
import static okhttp3.internal.http.StatusLine.HTTP_CONTINUE;
import static okhttp3.internal.http2.Header.RESPONSE_STATUS_UTF8;
import static okhttp3.internal.http2.Header.TARGET_AUTHORITY;
import static okhttp3.internal.http2.Header.TARGET_AUTHORITY_UTF8;
import static okhttp3.internal.http2.Header.TARGET_METHOD;
import static okhttp3.internal.http2.Header.TARGET_METHOD_UTF8;
import static okhttp3.internal.http2.Header.TARGET_PATH;
import static okhttp3.internal.http2.Header.TARGET_PATH_UTF8;
import static okhttp3.internal.http2.Header.TARGET_SCHEME;
import static okhttp3.internal.http2.Header.TARGET_SCHEME_UTF8;
/** Encode requests and responses using HTTP/2 frames. */
public final class Http2Codec implements HttpCodec {
private static final String CONNECTION = "connection";
private static final String HOST = "host";
private static final String KEEP_ALIVE = "keep-alive";
private static final String PROXY_CONNECTION = "proxy-connection";
private static final String TRANSFER_ENCODING = "transfer-encoding";
private static final String TE = "te";
private static final String ENCODING = "encoding";
private static final String UPGRADE = "upgrade";
/** See http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-8.1.3. */
private static final List HTTP_2_SKIPPED_REQUEST_HEADERS = Util.immutableList(
CONNECTION,
HOST,
KEEP_ALIVE,
PROXY_CONNECTION,
TE,
TRANSFER_ENCODING,
ENCODING,
UPGRADE,
TARGET_METHOD_UTF8,
TARGET_PATH_UTF8,
TARGET_SCHEME_UTF8,
TARGET_AUTHORITY_UTF8);
private static final List HTTP_2_SKIPPED_RESPONSE_HEADERS = Util.immutableList(
CONNECTION,
HOST,
KEEP_ALIVE,
PROXY_CONNECTION,
TE,
TRANSFER_ENCODING,
ENCODING,
UPGRADE);
private final Interceptor.Chain chain;
final StreamAllocation streamAllocation;
private final Http2Connection connection;
private Http2Stream stream;
private final Protocol protocol;
public Http2Codec(OkHttpClient client, Interceptor.Chain chain, StreamAllocation streamAllocation,
Http2Connection connection) {
this.chain = chain;
this.streamAllocation = streamAllocation;
this.connection = connection;
this.protocol = client.protocols().contains(Protocol.H2_PRIOR_KNOWLEDGE)
? Protocol.H2_PRIOR_KNOWLEDGE
: Protocol.HTTP_2;
}
@Override public Sink createRequestBody(Request request, long contentLength) {
return stream.getSink();
}
@Override public void writeRequestHeaders(Request request) throws IOException {
if (stream != null) return;
boolean hasRequestBody = request.body() != null;
List requestHeaders = http2HeadersList(request);
stream = connection.newStream(requestHeaders, hasRequestBody);
stream.readTimeout().timeout(chain.readTimeoutMillis(), TimeUnit.MILLISECONDS);
stream.writeTimeout().timeout(chain.writeTimeoutMillis(), TimeUnit.MILLISECONDS);
}
@Override public void flushRequest() throws IOException {
connection.flush();
}
@Override public void finishRequest() throws IOException {
stream.getSink().close();
}
@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
Headers headers = stream.takeHeaders();
Response.Builder responseBuilder = readHttp2HeadersList(headers, protocol);
if (expectContinue && Internal.instance.code(responseBuilder) == HTTP_CONTINUE) {
return null;
}
return responseBuilder;
}
public static List http2HeadersList(Request request) {
Headers headers = request.headers();
List result = new ArrayList<>(headers.size() + 4);
result.add(new Header(TARGET_METHOD, request.method()));
result.add(new Header(TARGET_PATH, RequestLine.requestPath(request.url())));
String host = request.header("Host");
if (host != null) {
result.add(new Header(TARGET_AUTHORITY, host)); // Optional.
}
result.add(new Header(TARGET_SCHEME, request.url().scheme()));
for (int i = 0, size = headers.size(); i < size; i++) {
// header names must be lowercase.
ByteString name = ByteString.encodeUtf8(headers.name(i).toLowerCase(Locale.US));
if (!HTTP_2_SKIPPED_REQUEST_HEADERS.contains(name.utf8())) {
result.add(new Header(name, headers.value(i)));
}
}
return result;
}
/** Returns headers for a name value block containing an HTTP/2 response. */
public static Response.Builder readHttp2HeadersList(Headers headerBlock,
Protocol protocol) throws IOException {
StatusLine statusLine = null;
Headers.Builder headersBuilder = new Headers.Builder();
for (int i = 0, size = headerBlock.size(); i < size; i++) {
String name = headerBlock.name(i);
String value = headerBlock.value(i);
if (name.equals(RESPONSE_STATUS_UTF8)) {
statusLine = StatusLine.parse("HTTP/1.1 " + value);
} else if (!HTTP_2_SKIPPED_RESPONSE_HEADERS.contains(name)) {
Internal.instance.addLenient(headersBuilder, name, value);
}
}
if (statusLine == null) throw new ProtocolException("Expected ':status' header not present");
return new Response.Builder()
.protocol(protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(headersBuilder.build());
}
@Override public ResponseBody openResponseBody(Response response) throws IOException {
streamAllocation.eventListener.responseBodyStart(streamAllocation.call);
String contentType = response.header("Content-Type");
long contentLength = HttpHeaders.contentLength(response);
Source source = new StreamFinishingSource(stream.getSource());
return new RealResponseBody(contentType, contentLength, Okio.buffer(source));
}
@Override public void cancel() {
if (stream != null) stream.closeLater(ErrorCode.CANCEL);
}
class StreamFinishingSource extends ForwardingSource {
boolean completed = false;
long bytesRead = 0;
StreamFinishingSource(Source delegate) {
super(delegate);
}
@Override public long read(Buffer sink, long byteCount) throws IOException {
try {
long read = delegate().read(sink, byteCount);
if (read > 0) {
bytesRead += read;
}
return read;
} catch (IOException e) {
endOfInput(e);
throw e;
}
}
@Override public void close() throws IOException {
super.close();
endOfInput(null);
}
private void endOfInput(IOException e) {
if (completed) return;
completed = true;
streamAllocation.streamFinished(false, Http2Codec.this, bytesRead, e);
}
}
}