OkHttp wiki官方文档翻译(二)

拦截器

拦截器是一个很强大的机制,可以监视、重写、重试请求。下面的例子是打印请求和响应的日志。

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();

    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    Response response = chain.proceed(request);

    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}

chain.proceed(request)的调用是每个拦截器实现类至关重要的一部分。这个简单的方法是所有的Http工作、产生满足请求的响应的地方。

拦截器可以被链式的串联。假设你有一个压缩拦截器和一个校验和拦截器:你需要决定数据是先压缩再校验,还是先校验再压缩。OkHttp使用一个列表来追踪拦截器,拦截器被顺序调用。


应用拦截器

拦截器可以被注册为应用拦截器或者网络拦截器。我们将使用日志拦截器来查看两者的不同。

通过okhttpClient.Builder的addInterceptor()方法可以添加应用拦截器。

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new LoggingInterceptor())
    .build();

Request request = new Request.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Response response = client.newCall(request).execute();
response.body().close();
http://www.publicobject.com/helloworld.txt被重定向到了https://publicobject.com/helloworld.txt, okhttp自动跟随重定向。我们的应用拦截器被调用一次,从chain.proceed()返回的响应是重定向之后的响应。

INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example

INFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

我们可以看到我们的请求被重定向了,因为response.request().url()和request.url()是不同的。两个INFO可以看出是两个URLS.

网络拦截器

注册一个网络拦截器也十分的相似。调用addNetworkInterceptor()方法而不是addInterceptor()方法:

OkHttpClient client = new OkHttpClient.Builder()
    .addNetworkInterceptor(new LoggingInterceptor())
    .build();

Request request = new Request.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Response response = client.newCall(request).execute();
response.body().close();
当我们运行这段代码,拦截器运行了两次,一次是最初的请求到http://www.publicobject.com/helloworld.txt 另一次是重定向到https://publicobject.com/helloworld.txt.

 

INFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=none protocol=http/1.1}
User-Agent: OkHttp Example
Host: www.publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip

INFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html
Content-Length: 193
Connection: keep-alive
Location: https://publicobject.com/helloworld.txt

INFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1}
User-Agent: OkHttp Example
Host: publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip

INFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

网络请求包含更多的数据,比如被OkHttp添加的Accept-Encoding:gzip头用于支持响应压缩。网络拦截器的链具有非空的连接,它可用于询问IP地址和连接到网络服务器的TLS配置。

选择应用和网络拦截器

每个拦截器链都有相应的优势。

应用拦截器

  • 不需要担心中间响应比如重定向和重试。
  • 一般只调用一次,即使Http响应来源于缓存。
  • 只需关注应用最初的意图,无需关心Okhttp注入的头信息,比如If-None-Match。
  • 允许短路和不调用Chain.proceed() 。
  • 允许重试,并多次调用Chain.proceed() 。

网络拦截器

  • 可以操作中间响应,比如重定向和重试。
  • 不会调用短路网络请求的缓存响应。
  • 关注在网络上的数据发送。
  • 访问携带请求的连接。
重写请求

拦截器可以添加、删除、替换请求头。也可以转换请求体。比如如果你知道Web服务器支持压缩,你可以使用一个应用拦截器去添加请求体压缩。

/** This interceptor compresses the HTTP request body. Many webservers can't handle this! */
final class GzipRequestInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request originalRequest = chain.request();
    if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
      return chain.proceed(originalRequest);
    }

    Request compressedRequest = originalRequest.newBuilder()
        .header("Content-Encoding", "gzip")
        .method(originalRequest.method(), gzip(originalRequest.body()))
        .build();
    return chain.proceed(compressedRequest);
  }

  private RequestBody gzip(final RequestBody body) {
    return new RequestBody() {
      @Override public MediaType contentType() {
        return body.contentType();
      }

      @Override public long contentLength() {
        return -1; // We don't know the compressed length in advance!
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
        body.writeTo(gzipSink);
        gzipSink.close();
      }
    };
  }
}
重写响应

相应的,拦截器可以重写响应头和转换响应体。但是这比重写请求要危险的多,因为这违反了Web服务器的期望。

在一个棘手的情况下,如果已经做好应对的后果,重写响应头是解决问题的有效方式。例如,您可以修复服务器的配置错误的Cache-Control响应头以便更好地响应缓存:

/** Dangerous interceptor that rewrites the server's cache-control header. */
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Response originalResponse = chain.proceed(chain.request());
    return originalResponse.newBuilder()
        .header("Cache-Control", "max-age=60")
        .build();
  }
};

OkHttp的拦截器在OkHttp2.2或更高版本可以使用。不幸的是,拦截器并不能与OkUrlFactory一起很好的工作,基于OkUrlFactory的类库也不能与拦截器很好的工作,包括Retrofit ≤ 1.8 and Picasso ≤ 2.4.

HTTPS


OkHttp 尝试平衡如下两个关注点:

  • 连接尽可能多的主机。包括运行最新版本的boringssl的主机和运行过时版本的OpenSSL主机。
  • 连接的安全性。包括远程Web服务器证书验证以及数据交换的私密性。

当与一个Https服务器打交道,OkHttp需要知道TLS版本和cipher suites。一个客户端想要最大化的连接性就必须包括废弃的TLS版本以及设计不精良的cipher suites。一个严格的客户端想要最大化的安全性需要限制使用最新版本的TLS以及最强劲的cipher suites。

Specific security vs. connectivity decisions are implemented by ConnectionSpec. OkHttp 内置了如下connection specs:

  • MODERN_TLS 是一个连接modern HTTPS 服务器的安全配置.
  • COMPATIBLE_TLS 是一个连接not current–HTTPS 服务器的安全配置. 
  • CLEARTEXT 是一个用于http://URLs的不安全的配置。

默认情况下, OkHttp 会尝试一个MODERN_TLS 连接, 其次如果modern configuration失败了,OkHttp 会尝试COMPATIBLE_TLS 连接.

在每一个发布的OKHttp版本中,TLS的版本和ciper suites支持的情况都会不一样。比如在okhttp 2.2 我们放弃支持SSL 3.0以规避POODLE 的攻击。还有在OKHttp2.3中我们放弃支持RC4.为了安全,请保持更新OKHttp至最新版本。 

你也可以使用你自己的TLS和cipher suies构建你自己的ConnectionSpec.例如,限制配置三个备受推崇的cipher suies。它的缺点是,它需要的Andr​​oid 5.0+和一个类似的网络服务器。

ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)  
    .tlsVersions(TlsVersion.TLS_1_2)
    .cipherSuites(
          CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
          CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
          CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
    .build();

OkHttpClient client = new OkHttpClient.Builder() 
    .connectionSpecs(Collections.singletonList(spec))
    .build();

Certificate Pinning证书锁定技术

默认情况下,OKHttp信任权威的证书颁发机构。这个策略最大化了连接性,但是受限于证书颁发机构发生的攻击比如2011 DigiNotar攻击。这里也假设你的Https 服务器安装的也是权威的证书颁发机构的证书。

使用CertificatePinner去限制什么样的证书及颁发机构可以被信任。Certificate Pinning 提高的安全性,但是却限制了你的服务器团队去更新他们TLS证书。Do not use certificate pinning without the blessing of your server’s TLS administrator!

 public CertificatePinning() {
    client = new OkHttpClient.Builder()
        .certificatePinner(new CertificatePinner.Builder()
            .add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
            .build())
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/robots.txt")
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    for (Certificate certificate : response.handshake().peerCertificates()) {
      System.out.println(CertificatePinner.pin(certificate));
    }
  }
定制信任的证书

如下代码展示了如何定制自己的证书。在此之前,, do not use custom certificates without the blessing of your server’s TLS administrator!

 private final OkHttpClient client;

  public CustomTrust() {
    SSLContext sslContext = sslContextForTrustedCertificates(trustedCertificatesInputStream());
    client = new OkHttpClient.Builder()
        .sslSocketFactory(sslContext.getSocketFactory())
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/helloworld.txt")
        .build();

    Response response = client.newCall(request).execute();
    System.out.println(response.body().string());
  }

  private InputStream trustedCertificatesInputStream() {
    ... // Full source omitted. See sample.
  }

  public SSLContext sslContextForTrustedCertificates(InputStream in) {
    ... // Full source omitted. See sample.
  }

Evens(再稳定版之后再做翻译)

Warning: This is a non-final API.

As of OkHttp 3.9, this feature is an unstable preview: the API is subject to change, and the implementation is incomplete. We expect that OkHttp 3.10 or 3.11 will finalize this API. Until then, expect API and behavior changes when you update your OkHttp dependency.

com.squareup.okhttp2是一个开放源代码的网络请求库,用于处理HTTP和HTTPS请求。它是Square公司开发的,并且提供了许多功能和灵活性来简化网络请求的处理。 OkHttp是一个多功能的库,支持与服务器的各种通信和交互。它支持GET、POST、PUT、DELETE等HTTP方法,并且可以发送和接收各种类型的数据,包括JSON、XML、图片等。它还提供了处理文件上传和下载的功能。 这个库具有高度可定制性,可以根据不同的需求进行配置。可以设置超时时间、缓存策略、重试策略等。它还支持添加自定义的拦截器来处理请求和响应,可以对请求进行修改或者添加自定义的头部信息。 OkHttp2支持异步请求和同步请求。异步请求使用回调机制,可以通过设置回调方法来处理服务器的响应。同步请求将会阻塞调用线程,直到服务器返回响应。 该库具有高性能和高效率。它使用连接池来复用连接,减少了连接的建立和关闭的时间。它还支持Gzip压缩和缓存等机制,可以减少带宽的使用。 OkHttp2还提供了一些额外的功能,如取消请求、重定向、Cookie管理等。它还支持HTTPS的安全连接,提供了验证证书的机制,可以保证通信的安全性。 总之,com.squareup.okhttp2是一个功能强大、易于使用的网络请求库。无论是开发Android应用程序还是服务端程序,它都是一个不错的选择。它具有丰富的功能和高度的定制性,同时也是一个性能优秀和稳定可靠的库。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值