1. History of OkHttp:

  • Initially, Square decided that the android solution didn’t work so well, so he wrapped it up and it worked
    • Slowly, they got rid of HttpClient,
    • And then he was bought by Google,
    • Now that we’re using the newer Android system (4.4), the internal HttpURLConnection implementation actually uses okHTTP code.
  • Okhttp is essentially a native tool that implements HTTP from beginning to end,
  • It also makes it easier for you to use HTTP, if you want cash, or cookies.
  • It’s not just a handy tool, it’s HTTP first and foremost.
  • But he also did all the TCP connections
  • He doesn’t rely on the Google stuff at all. Initially dependent, then not dependent, and finally Google took him.

2. What is it?

  • See the official website introductionsquare.github.io/okhttp/
    • It is an HTTP and Http2 client.
    • It was also mentioned in the introduction of Retrofit that it is type safe. (retrofit design principle (see you first read the source code)) because he did some extra work itself, and he receives the data processing, he’ll go to do will do transformation, you receive is a body, but you can switch to a User, this is related to type safety, how did you go I don’t have quoted you wrong.
    • Okhttp is just for HTTP, and it’s mostly for the lower level support, the upper level is just comfortable, but it’s not an upper level library.

3. How does it work?

  • See the website.
  • I first create a client, then newCall, execute.
  • Then we can’t use execute on Android, because it’s the same as Retrofit. Execute is synchronous, it doesn’t rotate to threads, but we have to put it in the background thread. I’m going to do another method which is enqueue
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  try (Response response = client.newCall(request).execute()) {
    returnresponse.body().string(); }}Copy the code
  • Write a demo, use is very simple.
OkHttpClient client = new OkHttpClient();
client.newCall(new Request.Builder().url("http://api.github.com").build())
        .enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {}@Override
            public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {}});Copy the code
  • The first step is to start him off,
  • And then I’m going to set what URL I want to go to,
  • So it defaults to a GET request, so you don’t have to write your get, it defaults to GET, just as if you were writing it in the browser.
new Request.Builder().url("http://api.github.com").build()
Copy the code
  • So in the brackets, you create a request, each time you have to recreate the request, and then the life cycle of the request, how to call the data, how to access it, how to return the result, that’s what OKHTTP manages for you, but every time you do that, you have to create a new request, which is called a request.
  • Then, enqueue is doing requests asynchronously.

4. Source code interpretation

  • Mainly about the structure:
    • How does OKHTTP implement HTTP, how does OKHTTP implement TCP, how does OKHTTP implement HTTPS

Starting with newcall

  • So we start with newCall. What is newcall?
    • He creates a call, the client creates a call using this method, and then uses this call to conduct real network interaction.
    • Newcall passes a parameter called request, which you spell out yourself.
    • So this method is, I pass in a request, and I use that request to create a call, to create a pending network request.
  • Point in
@Override public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}
Copy the code
  • He’ll call another method, realcall.newRealCall (), and he’ll return a RealCall,
  • I asked for a call, and I returned a realCall, which is obviously an implementation of a call.
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  // Safely publish the Call instance to the EventListener.
  RealCall call = new RealCall(client, originalRequest, forWebSocket);
  call.eventListener = client.eventListenerFactory().create(call);
  return call;
}
Copy the code
  • The first line is to create the object, using the parameters you passed in, to create a call
    • Client is the main manager, total configuration, nothing good to resolve. More on that later.
    • OriginalRequest is a request sent by you. Why is it called originalRequest?
      • This is related to the chain of responsibility that we’ll talk about in a minute, he’s the original request. It’s going to be different over the course of the chain.
    • ForWebSocket is more distant,
      • He means that you are not a WebSocket call.
      • WebSocket is an extension of HTTP. He can fiddle a little bit between your HTTP request and your response, you’re still an HTTP, but I fiddle a little bit, I make your interaction,
        • What state is he in? We talked earlier about how HTTP is a mode, it’s a CS mode. A client-server mode in which the client sends a request, the server receives it and returns a response to the request. You send one more and I’ll come back. It’s kind of back and forth, back and forth. The server cannot make the request voluntarily. I’m asking the client for the data, or I’m pushing it directly to the client, which doesn’t work.
        • WebSocket is an extension of HTTP. He didn’t extend the standard, he just changed the implementation. Then you can let the server do the push to the client.
        • But we use traditional HTTP on Android. Very few webSockets are useful. Who uses WebSocket? The ones that do the trading, not the ones that do the trading, but the ones that do the trading of stocks and securities, the ones that do the trading, the ones that do the trading of virtual currencies, they need to refresh their data on a regular, frequent basis. But if it’s polling every time, it costs a lot of traffic, it costs a lot of power, so they’re going to use WebSocket, and they’re going to do push that way.
        • So it’s not usually used.
  • In the second line, an eventListener is created.
    • There are critical points in your OKHTTP process, such as when a TCP connection is established, all connections are TCP connections, or when an SSL connection has no HTTP connection.
      • What is a connection? I’m a machine, you’re a machine, and the two of us interact, and I need to be able to remember who you are. So you text me and I’ll know straight away, you don’t have to say who you are, it’s a connection.
      • HTTP is inherently connectionless, and all connections are either TCP connections or SSL connections (secure connections, which are the ones used for HTTPS).
    • There are many, many states in the connection process, where the connection is established, the request is started, and the response is returned. These are all points in time that you can record in eventListener and use it to mark the response.

Look at the enqueue

  • Point in
public interface Call extends Cloneable {...void enqueue(Callback responseCallback); . }Copy the code
  • This call is not a call to retrofit, but they are both an interface, so what do I think of its implementation? RealCall RealCall RealCall RealCall RealCall RealCall RealCall RealCall RealCall RealCall
@Override public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  eventListener.callStart(this);
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Copy the code
  • Not much. I added an event to the listener
eventListener.callStart(this);
Copy the code
  • There’s another line where he’s handing it over to someone else.
  • To a dispatcher, to the Enqueue,
  • Then it also generates a new object AsyncCall, an asynchronous call
client.dispatcher().enqueue(new AsyncCall(responseCallback));
Copy the code
  • So what’s dispatcher() and AsyncCall
public Dispatcher dispatcher(a) {
  return dispatcher;
}
Copy the code
  • Dispatcher returns a Dispatcher object, so let’s look at dispatcher
public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  /** Executes calls. Created lazily. */
  private @NullableExecutorService executorService; . }Copy the code
  • The Dispatcher is actually a thing that manages threads,
    • Each of your new requests, your new request and its response process, your request and its return process, it needs a separate thread, so that your unused requests don’t get blocked from each other.
    • How do you do that? It relies on thread control. What about thread control? This is the Dispatcher. Its internal implementation uses execute.
  • For the moment, just know that it is used to manage threads. With it, multiple threads can be simply controlled. I can have as many as I want, and I can have as few as I want, and I can have as few as I want.
 private int maxRequests = 64;
 private int maxRequestsPerHost = 5;
Copy the code
  • The Dispatcher has two default things

    • MaxRequests:
      • When my total connection reaches 64, I don’t want to make new requests, I’ll wait,
    • MaxRequestsPerHost:
      • When I get to five requests for a host, I stop making new requests for that host. For example, now I only request two hosts, a host has two requests, B host has five requests, at this time the user will send a request to b host, I put her first.
      • Is to prevent a certain website, a certain server to impose too much pressure.
  • The enqueue process, the enqueue process, the enqueue process, it doesn’t have each of your requests executed in order, it has them split and queued, but at some point they wait. Their degree is set up right here.

  • Both values can be set. If you want requests to be processed sequentially, set maxRequests to 1.

  • Now that we know what a Dispatcher is, let’s take a look at its enqueue, and we’ve already told you what an enqueue is, which is to have them execute side by side, but when you get to the top, you put them in the queue.

synchronized void enqueue(AsyncCall call) {
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);
  } else{ readyAsyncCalls.add(call); }}Copy the code
  • If consists of two steps:
    • If I don’t exceed my limit, then I just go ahead and do it,
    • If I exceed my limit, I put it in the ready queue. A waiting queue, ready to make a request.
  • Another thing to look at, its argument is AsyncCall, what does this AsyncCall do?
    • It should have a run method, but it was not found. Thread control is executed in the runnable’s run method
final class AsyncCall extends NamedRunnable {... }Copy the code
  • It’s not in there. Go to the parent.
public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run(a) {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally{ Thread.currentThread().setName(oldName); }}protected abstract void execute(a);
}
Copy the code
  • The superclass has a run method, but I noticed that the run method still executes execute(). Notice that execute() is a different class than the previous dispatcher. Who is it? Execute () is the RealCall AsyncCall implementation.
@Override protected void execute(a) {
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain();
    if (retryAndFollowUpInterceptor.isCanceled()) {
      signalledCallback = true;
      responseCallback.onFailure(RealCall.this.new IOException("Canceled"));
    } else {
      signalledCallback = true;
      responseCallback.onResponse(RealCall.this, response); }}catch (IOException e) {
    if (signalledCallback) {
      // Do not signal the callback twice!
      Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
    } else {
      eventListener.callFailed(RealCall.this, e);
      responseCallback.onFailure(RealCall.this, e); }}finally {
    client.dispatcher().finished(this); }}Copy the code
  • There are several layers to this, but the fact is that when you call enqueue externally, you end up in the Execute () method of the AsyncCall implementation in the RealCall.
client.newCall(new Request.Builder().url("http://api.github.com").build())
        .enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {}@Override
            public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {}});Copy the code
  • What AsyncCall’s execute() method does is all in this method:
 Response response = getResponseWithInterceptorChain();
Copy the code
  • Get the response, through the interceptor chain, hasn’t executed yet, how is there a response? It’s all in here, but it’s going to go a little bit deeper. I’ll talk about it later.

  • Now, to summarize, if you’re using the enqueue approach,

    • So he’ll go into your realcall,
    • Call your Dispatcher thread management tool enqueue,
    • Which triggers your AsyncCall’s run method, which triggers its execute method,
    • Finally want to talk to me later getResponseWithInterceptorChain ().
  • So this is complete here. How is it complete? You first put your request in the background, then do the actual network request, and then return the network request result. It’s a complete process.

  • In addition to enqueue, there’s a execute, which we use very little, but not completely, because sometimes we’re already in the background. For example, now my network request failed, the network request failed for some reason, like I don’t have enough permissions. I need to get my token now, go online and get my token, get my token, and then proceed with the request, so I’m in the background now, so I don’t need enqueue, I need execute.

  • So I’m just going to take a quick look at what execute does, which is to make a network request directly.

@Override public Response execute(a) throws IOException {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  eventListener.callStart(this);
  try {
    client.dispatcher().executed(this);
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
  } catch (IOException e) {
    eventListener.callFailed(this, e);
    throw e;
  } finally {
    client.dispatcher().finished(this); }}Copy the code
  • Direct call getResponseWithInterceptorChain ().

So we have the structure

  • This is his big structure,

    • Create a realcall,
    • Enqueue or execute to make a network request,
    • Then return the response.
  • Now two things:

    • Okhttp in the practical point of view, whether in the development of practical, or let us understand the close relationship between okHTTP and HTTP, and let you deepen the understanding of HTTP, no matter from which point of view, we need to look at the okHttpClient method, he inside a variety of configuration items, what are? What do they do? This is one.
OkHttpClient client = new OkHttpClient();
Copy the code
  • Another is to interpret getResponseWithInterceptorChain () this method. This approach is the technical heart of OKHTTP. If you want to understand how it works, if you want to use it better, or if you want to look good in an interview, this is something you need to understand.

What is the configuration of OkHttpClient

public class OkHttpClient implements Cloneable.Call.Factory.WebSocket.Factory {... }Copy the code
  • I don’t know a few of them, but I’ve used them, and I haven’t used them but I think they’ll be useful.
public static final class Builder {
  Dispatcher dispatcher;
  @Nullable Proxy proxy;
  List<Protocol> protocols;
  List<ConnectionSpec> connectionSpecs;
  final List<Interceptor> interceptors = new ArrayList<>();
  final List<Interceptor> networkInterceptors = new ArrayList<>();
  EventListener.Factory eventListenerFactory;
  ProxySelector proxySelector;
  CookieJar cookieJar;
  @Nullable Cache cache;
  @Nullable InternalCache internalCache;
  SocketFactory socketFactory;
  @Nullable SSLSocketFactory sslSocketFactory;
  @Nullable CertificateChainCleaner certificateChainCleaner;
  HostnameVerifier hostnameVerifier;
  CertificatePinner certificatePinner;
  Authenticator proxyAuthenticator;
  Authenticator authenticator;
  ConnectionPool connectionPool;
  Dns dns;
  boolean followSslRedirects;
  boolean followRedirects;
  boolean retryOnConnectionFailure;
  int connectTimeout;
  int readTimeout;
  int writeTimeout;
  intpingInterval; . }Copy the code

1.Dispatcher thread scheduling

  • First of all, this Dispatcher, it’s mentioned above.
  • It’s going to control threads, schedule threads, and then use different threads to make network requests,
  • It also has a performance balance, and after a certain amount I don’t do it, I take a break, wait a minute.

2. The Proxy agent

  • The second is a Proxy, which is a Proxy that you can configure yourself.
  • To understand him, in fact, is mainly to have an understanding of the concept. What is a Proxy?
    • For example, now I want to visit a foreign website, this foreign website cannot be connected from my home network for some reasons, and THEN I need to connect from another place. Abroad have two servers, one is from my family don’t know the reason why even less than, but I don’t know this server to fail, I hope that even in the past, I from my host to connect to another host, the host may be abroad, may also be at home, doesn’t matter, all in all, the intermediary machine, he can connect to the target machine, so I go to my intermediary machine, He is my agent, I write clearly what I want to do, who I want to access, I give it to the proxy server, and the proxy server asks me to do it, and this is a reasonable, legal, required intermediary, and I know for myself exactly what role he plays.

3.Protocol Protocol version

  • What is Protocol? Just click in and find out. It is the version of the protocol that you list to your client okHttpClient that you support. So that your okHTTP his work process he knows what his options are and he will do the deployment himself.
  • SPDY is a lot like Http2, it’s a precursor to Http2, and HTTP2 borrowed a lot from SPDY. Spdy was used by Google at the beginning, and Google used it by itself, but gradually everyone absorbed it, improved it, and became the standard. This standard is called HTTP2. So spdy is actually obsolete now.
public enum Protocol {
  HTTP_1_0("HTTP / 1.0"),
  HTTP_1_1("HTTP / 1.1"),
  SPDY_3("Spdy / 3.1"),
  HTTP_2("h2"),... }Copy the code
  • Protocol is such a thing, so it should be a list, multiple options. Your client can choose on site, just like our browser, which supports HTTP1.0, 1.1, 1.2, and 2.0. Older browsers may not be able to support 2.0.

4.ConnectionSpec Connection standard

  • Look at the name, connection specifications? Point in.
public final class ConnectionSpec {

  // This is nearly equal to the cipher suites supported in Chrome 51, current as of 2016-05-25.
  // All of these suites are available on Android 7.0; earlier releases support a subset of these
  // suites. https://github.com/square/okhttp/issues/1972
  private static final CipherSuite[] APPROVED_CIPHER_SUITES = new CipherSuite[] {
      CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
      CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
      CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
      CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
      CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
      CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,

      // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll
      // continue to include them until better suites are commonly available. For example, none
      // of the better cipher suites listed above shipped with Android 4.4 or Java 7.
      CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
      CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
      CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
      CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
      CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
      CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
      CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
  };

  /** A modern TLS connection with extensions like SNI and ALPN available. */
  public static final ConnectionSpec MODERN_TLS = new Builder(true)
      .cipherSuites(APPROVED_CIPHER_SUITES)
      .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
      .supportsTlsExtensions(true)
      .build();

  /** A backwards-compatible fallback connection for interop with obsolete servers. */
  public static final ConnectionSpec COMPATIBLE_TLS = new Builder(MODERN_TLS)
      .tlsVersions(TlsVersion.TLS_1_0)
      .supportsTlsExtensions(true)
      .build();

  /** Unencrypted, unauthenticated connections for {@code http:} URLs. */
  public static final ConnectionSpec CLEARTEXT = new Builder(false).build(); . }Copy the code
  • So that’s the configuration, do you want to use HTTP or HTTPS? If you are using HTTPS, your version is ssl3.0, tls1.0, TLS 1.1, TLS 1.2, and so on. If you are using HTTPS, there is also a list of the TLS available to you.
CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
Copy the code
  • We talked about this earlier when we talked about HTTPS.What exactly is Https? (Https encryption process fully parsed)), what do you send when the client notifies the server that you want to set up a secure connection? You send a CIPHER_SUITES. What’s in a CIPHER_SUITES?
    • RSA: indicates an asymmetric encryption algorithm
    • AES_256: indicates a symmetric encryption algorithm
    • SHA384: hash algorithm
  • In addition to CIPHER_SUITES, a TLS version will be sent. 1.3, 1.2, 1.1, 1.0
  public static final ConnectionSpec MODERN_TLS = new Builder(true)
      .cipherSuites(APPROVED_CIPHER_SUITES)
      .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
      .supportsTlsExtensions(true)
      .build();

Copy the code
  • This is a unified support because it is an acceptable solution, what can you support, what can you accept? As for what the other person is offering you, what the other person is willing to accept, you finally negotiate a plan, they are fixed. For example, in the end you decide to use either 1.1 or 1.2, you can’t use both 1.1 and 1.2, but during the discussion, you will be given a list to choose from.

  • COMPATIBLE_TLS is for older servers and is not used in general.

public static final ConnectionSpec COMPATIBLE_TLS = new Builder(MODERN_TLS)
    .tlsVersions(TlsVersion.TLS_1_0)
    .supportsTlsExtensions(true)
    .build();
Copy the code
  • What CLEARTEXT means needs to be explained separately.
public static final ConnectionSpec CLEARTEXT = new Builder(false).build();
Copy the code
  • CLEARTEXT means CLEARTEXT. Cleartext, for ConnectionSpec, for this connection standard, is exactly the same as HTTP.
  • Because HTTP means that your data is transparent, it’s open, it’s not encrypted. Conversely, when is it plaintext? It’s not encrypted.

5. Interceptors, networkInterceptors

  • Leave the two Interceptors alone for now. Or later when it comes to method, getResponseWithInterceptorChain ().
  • Interceptors and networkInterceptors are two Interceptor lists that can be inserted into the chain. What do they do separately? What’s the difference? I’ll talk about that in a second.

6.eventListenerFactory

  • Not to mention, the eventListener is what makes the record, and the Factory is what creates the listener.

7.proxySelector

  • I don’t know what it is. It’s not used.

8.cookieJar

  • The cookieJar sometimes really feels like our Sinology programs are getting the short end of the stick.
    • The word cookieJar doesn’t need to be explained at all, but the Chinese do.
    • First of all, cookie is a complete mechanism between the browser and the server, the server to save things, he lets the server to save, is to use cookies.
    • But what does cookie actually mean? Cookies, cookies.
    • What is a JAR? We Java programmers look at this and think it’s a Java JAR package. Isn’t Java packaging a JAR? Later android packaging is not an AAR? Isn’t a JAR a Java package? We would feel like this, and you cookieJar we wouldn’t understand. But what is actually a JAR? Jar is a jar, and the foreign children, have a own cookie jar, his mother gave him the oven for him to do a lot of cookies, after eating, where to put? It’s in his cookieJar.
  • This is actually a very image of things, he is actually a memory of our cookies, our cookies stored in the local, or stored in memory? There is no default implementation of okHTTP. He doesn’t care. It’s all about his character. Okhttp they think, we do not need to do the client is to achieve cookie, so you want to achieve, you achieve, I do not achieve, we do android we do not use cookie. They have the personality, but they will give you the interface to implement it, and you can develop it yourself. That’s what you end up with. Half-cooked. Can use, but you want to use good words, oneself specify how he should save, how take.

9.Cache

  • This is easy to understand, it’s our HTTP cash, but I don’t understand InternalCache.

10.SocketFactory

  • We do android people, may not know a lot about Socket, some people may use it, what is Socket? A socket is our TCP port, which is a (IP-port) pair.

    • I should mention that the default port is 80
    • hencoder.com:80/
    • Or you could write 8080, 8088, whatever you want, as long as you don’t go over his upper limit. This port is a TCP port.
    • When you’re done parsing it, it might give you an IP
    • https://222.222.222.222:80/
    • 222.222.222.222:80. They’re not actually in the first order
      • 222.222.222.222 is the IP address
      • 80 the port
  • The SocketFactory is used to create this port. What do you mean by creating a port?

    • In fact, it is connected to the other server, to get the connection port, to get the server can write things, can read from the server that so-called port, is this socket.
  • SSLSocketFactory is the SSL connection’s factory. Why separate it?

    • Because sockets are TCP things, and SSL sockets are ACTUALLY SSL things, so it’s not one layer. So they have to do it separately.
    • They all have different principles. Including the way the connection is made.
    • How to set up TCP? How to establish SSL with a three-way handshake?
    • A lot of, like, key exchange… Their process is completely different.
  • HTTP has no ports. There is no such thing as an HTTP port. Because he’s not connection-oriented, he’s not connection-oriented. Sometimes we say it’s an HTTP port, but it’s a TCP port. HTTP doesn’t have a port.

11.CertificateChainCleaner

  • They’re the certificates that we take off the server, and sometimes they can be many, many, many, many, many certificates. Certificate embedded certificate, a certificate chain.
  • Sometimes there will be some unrelated certificates issued. So what will CertificateChainCleaner do?
    • He’s going to sort all of this stuff out of you, and it’s going to be a chain, or a sequence.
    • The first of these sequences is the certificate of the other site, then one by one, and finally the local root certificate that you trust.
    • It’s easy to verify, but it has nothing to do with us. It’s pretty low down. I’m just letting you know what he is. Why should I let you know what he is? Why? Review of HTTP. It’s hard to remember just the principle. If you work with the code, isn’t that what OKHTTP does?

12.HostnameVerifier

  • It’s for HTTPS. How does it work? You need to verify that the host is the one you want to access. You’ll get a better idea if you click in.
public interface HostnameVerifier {
    /**
     * Verify that the host name is an acceptable match with
     * the server's authentication scheme.
     *
     * @param hostname the host name
     * @param session SSLSession used on the connection to host
     * @return true if the host name is acceptable
     */
    public boolean verify(String hostname, SSLSession session);
}
Copy the code
  • HostnameVerifier has a verify method. Option + Command + left mouse button, click in to see the interface implementation
@Override
public boolean verify(String host, SSLSession session) {
  try {
    Certificate[] certificates = session.getPeerCertificates();
    return verify(host, (X509Certificate) certificates[0]);
  } catch (SSLException e) {
    return false; }}Copy the code
  • Verify the first certificate. Why do you validate the first certificate? Because this is the certificate of the other party’s website
    Certificate[] certificates = session.getPeerCertificates();
    return verify(host, (X509Certificate) certificates[0]);

Copy the code
  • In this case, the value should be IpAddress or Hostname
public boolean verify(String host, X509Certificate certificate) {
  return verifyAsIpAddress(host)
      ? verifyIpAddress(host, certificate)
      : verifyHostname(host, certificate);
}
Copy the code
  • Click on it, and eventually, after all sorts of processing and troubleshooting, it will compare the name of the other server to the name of the one we’re accessing.
  • If it does, it’s definitely not a third party man-in-the-middle attack.
return hostname.equals(pattern);

Copy the code

13.CertificatePinner

  • This might be a useful point for us, because it’s used for self-signing. Some people might use a subsignature in a company, but I’ll say an easier way to use self-signature. Directly using CertificatePinner can be achieved.
  • First, what is Certificate? Is the certificate. Pinner pins. CertificatePinner is a certificate holder.
    • Sometimes your online certificate fails to be verified locally, either because it is self-signed or because your local certificate authority has not updated it. In your local authentication failed. But you know very well that I am the developer of this website, or I am the programmer of this website. The engineer of our company can clearly tell me the public key of this certificate, and he told me that this is definitely the public key of our certificate. I record the information of this certificate in the local, I go to download the certificate, after the next I go to contrast, two certificates as long as the same not over, with the certificate agency what has nothing to do with. I just need one. I don’t care about cryptography or anything, I just need to make sure they’re one. So how do I compare?
    • I download the certificate chain from the remote end, and record the public key of each of his certificates in my local. And then I matched it with the real-time request. In the process of establishing our connection, the process has not yet reached HTTP, but only TLS. When I obtain this certificate, I will check whether its public key information is the same as the public key information stored locally. If it is the same, it will be ok.
  • How does this work? The code in the example is pasted directly here. change
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
// You can add more than one, the more the easier
        .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
        .build();
OkHttpClient client =new OkHttpClient.Builder()
        .certificatePinner(certificatePinner)
        .build();
Request request = new Request.Builder()
        .url("https://" + hostname)
        .build();
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {}@Override
    public void onResponse(Call call, Response response) throws IOException {}});Copy the code

14.Authenticator: proxyAuthenticator, Authenticator

  • They are used to write the Authorization headers for login Authorization. What do we add this to?
    • When you do not have enough permissions, he will give you a wrong report, will give you back a 401… This error okHTTP will automatically stop you. If you have configured authenticator with authenticator(), when you encounter this error later it will automatically pop up and automatically call the authenticator callback method. At this point, you can ask for your token. Or you can add basic Authorization for your password and username, and just add them in.
  • How does it work?
OkHttpClient client =new OkHttpClient.Builder()
        .certificatePinner(certificatePinner)
        .authenticator(new Authenticator() {
            @javax.annotation.Nullable
            @Override
            public Request authenticate(Route route, Response response) throws IOException {
                return response.request().newBuilder().addHeader("Authorization"."Base64 for Basic username password").build();
            }
        })
        .build();
Copy the code

15. ConnectionPool connection pool

  • Connection pools, thread pools, are all collections with caching. For example, if I set the connection pool to a maximum of 20 and an initial value of 5, then I have five threads that I can hold and use at any time, or connections that I can hold and use at any time. The advantage of having a connection pool like this is that I can use it at any time, so I don’t have to create it on site. In the same way, he has a ceiling so he doesn’t overuse my resources.

16.Dns

  • What is DNS? Just a quick look at his implementation
 Dns SYSTEM = new Dns() {
  @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException {
    if (hostname == null) throw new UnknownHostException("hostname == null");
    try {
      return Arrays.asList(InetAddress.getAllByName(hostname));
    } catch (NullPointerException e) {
      UnknownHostException unknownHostException =
          new UnknownHostException("Broken system behaviour for dns lookup of " + hostname);
      unknownHostException.initCause(e);
      throwunknownHostException; }}};Copy the code
  • The key is this row
Arrays.asList(InetAddress.getAllByName(hostname));
Copy the code
  • It uses the systematic DNS method to get DNS, to get a list of IP addresses based on your domain name. It’s a list. A domain name may correspond to multiple IP addresses. This is DNS. You can just use it. When do you sometimes need to match yourself? It’s an IP that you don’t want to parse, you want to point it to yourself, and that’s very rare.

17. (Boolean) directs to followSslRedirects

  • Boolean “followRedirects”, which means if I need to redirect, do I need to redirect? So okHTTP gives me two choices whether you want to jump or not, and the default is jump.
  • “Do you want to jump?” “Do you want to jump?” “Do you want to jump?” “Do you want to jump?”

Why do you want to set this separately? Because they do have some security risks.

18. (Boolean) retryOnConnectionFailure

  • Do you want to try again when your connection fails?
  • But it’s not just that the connection failed, it’s that your request failed and you want to rerequest it.
  • However, the request to return 404,500 does not count. This is another example.

19. (int) connectTimeout, readTimeout, writeTimeout

  • ConnectTimeout: Indicates that the TCP connection time has timed out.
  • ReadTimeout: Wait time to download the response
  • WriteTimeout: The time required to write a request

20. (int) pingInterval

  • PingInterval is for WebSocket,
  • WebSocket uses HTTP to create a push, two-way interaction channel
    • This thing needs a long connection, it needs to send a little heartbeat message, it needs to confirm, it needs to make that connection permanent,
    • PingInterval indicates the ping interval. Why is it called ping?
      • They made the connection with two things, one called Ping and one called Pong. Ping pong, ping pong, that’s what it’s essentially. I will send a ping to you, and you will send a Pong after receiving it, so that I can know that you are not disconnected.

Core: getResponseWithInterceptorChain () in the chain of responsibility design pattern

  • So we just saw that both synchronous and asynchronous requests have this, so let’s go ahead and see how it works.
Response getResponseWithInterceptorChain(a) throws IOException {
  // Build a full stack of interceptors.
  List<Interceptor> interceptors = new ArrayList<>();
  interceptors.addAll(client.interceptors());
  interceptors.add(retryAndFollowUpInterceptor);
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  interceptors.add(new CacheInterceptor(client.internalCache()));
  interceptors.add(new ConnectInterceptor(client));
  if(! forWebSocket) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor(forWebSocket));

  Interceptor.Chain chain = new RealInterceptorChain(interceptors, null.null.null.0,
      originalRequest, this, eventListener, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());

  return chain.proceed(originalRequest);
}
Copy the code
  • And what that entails, to review, is taking your prepared request and making a web request and getting a response. This thing does a lot of work. It even builds its own TCP connection. So what’s the whole process?
  • If you look at these lines, there’s not a lot of lines, but each line has a different job, so it’s pretty full. These lines of code are the technical heart of OKHTTP. Just to give you a general idea.

Trunk: In three steps

  • So the first thing you do is you create this list, you enrich this list.
 List<Interceptor> interceptors = new ArrayList<>();
  interceptors.addAll(client.interceptors());
  interceptors.add(retryAndFollowUpInterceptor);
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  interceptors.add(new CacheInterceptor(client.internalCache()));
  interceptors.add(new ConnectInterceptor(client));
  if(! forWebSocket) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor(forWebSocket));
Copy the code
  • Next comes another line of code that creates a chain called the RealInterceptorChain.
  Interceptor.Chain chain = new RealInterceptorChain(interceptors, null.null.null.0,
      originalRequest, this, eventListener, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());

Copy the code
  • It then calls its proceed method after the chain is created.
  return chain.proceed(originalRequest);
Copy the code
  • Let’s talk about what these three steps are and what they do.

Chain of responsibility

  • This is my Interceptor, called I, and I’m going to create six of them, so this is my chain. What is that list? Needless to say, a list is used to create each node in the chain. When the final list is ready, it is created as a chain. Proceed of the chain is then called.

  • My whole process what does he use the chain for? It’s used for making web requests.
  • How does the actual network request him to do it? It has a direction. I walked all the way in this direction, like a production line. Send it, send it back.

  • Why a chain? So let me give you an example of what this chain does.
    • Burger someone order, he called me, and what is your address told me, I was the Hamburg master, is also a shop owner, I put the Hamburg ready, I give store is responsible for the distribution of Hamburg, the Hamburg he waited, later meals member of our store, he gave the Hamburg to room, room part to ride a bike to order people, then the man open the door, He takes the burger, he gives the money to the delivery guy, who takes a dollar off the delivery and gives the rest to the guy who distributes the burger, and then she gives the immortal money to me, who owns the place.
    • This is a chain, and I’m the beginning of the chain. I cook the burger, hand it over and finally get the money. The clerk in the store was responsible for handing over, handing over to the delivery man, and handing over to me the money that the delivery man had taken back. The deliveryman is responsible for delivering the burger and receiving the money, leaving a dollar behind and giving the rest back to the clerk. And the end point is the person who ordered the meal, and he did two things, one was to receive the hamburger, and the other was to give the money to the delivery man. This is a chain.
  • When it’s more complex, you break it down into chains, and each node does a different thing. Each node is an interceptor.
    • Why is it called chain? Why is it called interceptor.chain? That’s what it does. It’s not that he’s going to stand in your way, but he’s going to have to do something extra. Hold on, let me work on it. Before you do it, after you do it, this is his model of the interceptor.
  • To be specific, what does our Interceptor.Chain do?

proceed()

  • Chain.proceed () What is he?
  • I go from this point, to here, and then back.

  • What does that mean?
    • It is from the start to here, the work from the first Interceptor, to the second Interceptor, and then the first Interceptor wait, wait for the work back, do the return processing.
    • Chain-.proceed () just means that your chain is going forward. For me, it’s about passing the burger on to the next person, and you do your job. For each node, it’s doing, waiting, and doing.
  • Let’s talk about each node.
  • I’ll leave these two out of the picture, because they are self-matched and they don’t have them, so I’ll tell you what I have.
interceptors.addAll(client.interceptors());
interceptors.addAll(client.networkInterceptors());
Copy the code

Interceptor Interceptor

1. Call RetryAndFollowUpInterceptor and proceed

- First, retry is to retry and FollowUp is to follow the redirected link.Copy the code
 interceptors.add(retryAndFollowUpInterceptor);
Copy the code
  • Jump over and have a look. His key is the Intercept method.
public final class RetryAndFollowUpInterceptor implements Interceptor {...@Override public Response intercept(Chain chain) throws IOException {
  Request request = chain.request();
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Call call = realChain.call();
  EventListener eventListener = realChain.eventListener();

  StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
      createAddress(request.url()), call, eventListener, callStackTrace);
  this.streamAllocation = streamAllocation;

  int followUpCount = 0;
  Response priorResponse = null;
  while (true) {
    if (canceled) {
      streamAllocation.release();
      throw new IOException("Canceled");
    }

    Response response;
    boolean releaseConnection = true;
    try {
      response = realChain.proceed(request, streamAllocation, null.null);
      releaseConnection = false;
    } catch (RouteException e) {
      // The attempt to connect via a route failed. The request will not have been sent.
      if(! recover(e.getLastConnectException(), streamAllocation,false, request)) {
        throw e.getLastConnectException();
      }
      releaseConnection = false;
      continue;
    } catch (IOException e) {
      // An attempt to communicate with a server failed. The request may have been sent.
      booleanrequestSendStarted = ! (einstanceof ConnectionShutdownException);
      if(! recover(e, streamAllocation, requestSendStarted, request))throw e;
      releaseConnection = false;
      continue;
    } finally {
      // We're throwing an unchecked exception. Release any resources.
      if (releaseConnection) {
        streamAllocation.streamFailed(null); streamAllocation.release(); }}// Attach the prior response if it exists. Such responses never have a body.
    if(priorResponse ! =null) {
      response = response.newBuilder()
          .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
          .build();
    }

    Request followUp = followUpRequest(response, streamAllocation.route());

    if (followUp == null) {
      if(! forWebSocket) { streamAllocation.release(); }return response;
    }

    closeQuietly(response.body());

    if (++followUpCount > MAX_FOLLOW_UPS) {
      streamAllocation.release();
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }

    if (followUp.body() instanceof UnrepeatableRequestBody) {
      streamAllocation.release();
      throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
    }

    if(! sameConnection(response, followUp.url())) { streamAllocation.release(); streamAllocation =new StreamAllocation(client.connectionPool(),
          createAddress(followUp.url()), call, eventListener, callStackTrace);
      this.streamAllocation = streamAllocation;
    } else if(streamAllocation.codec() ! =null) {
      throw new IllegalStateException("Closing the body of " + response
          + " didn't close its backing stream. Bad interceptor?"); } request = followUp; priorResponse = response; }}...Copy the code
  • What does he do?
    • He does three things, which is preparation, handing it over to the next one and waiting for the next one to come back, and follow-up after he comes back.
    • The first one is the initial preparation StreamAllocation, StreamAllocation listen to this, it’s a key concept for OKhttp.
 StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
      createAddress(request.url()), call, eventListener, callStackTrace);
Copy the code
  • Click here to see the StreamAllocation comment
Connections:</strong> physical socket connections to remote servers. These are
potentially slow to establish so it is necessary to be able to cancel a connection
currently being connected.
Streams:</strong> logical HTTP request/response pairs that are layered on
connections. Each connection has its own allocation limit, which defines how many
concurrent streams that connection can carry. HTTP/1.x connections can carry 1 stream
at a time, HTTP/2 typically carry multiple.
Calls:</strong> a logical sequence of streams, typically an initial request and
its follow up requests. We prefer to keep all streams of a single call on the same
connection for better behavior and locality.
Copy the code
  • Okhttp has three key concepts:
    • Connection: A Connection between me and the server.
    • Stream: I make a request, I might send a message to the server, I might receive a message, there’s a pair, this pair is called Stream.
    • Call: I now want to access an HTTPS URL, I make a request and I get a response and that’s called a Call.
  • Our call might do a jump, it might do a redirect. So a call might have multiple streams.
  • Next RetryAndFollowUpInterceptor pre – work, basically no pre – work, because as a retry and redirection, to lead work? It is mainly the post work.
this.streamAllocation = streamAllocation;

  int followUpCount = 0;
  Response priorResponse = null;
  while (true) {
    if (canceled) {
      streamAllocation.release();
      throw new IOException("Canceled");
    }

    Response response;
    boolean releaseConnection = true;
Copy the code
  • What is postposition? You have to do this first. So you call proceed again here
response = realChain.proceed(request, streamAllocation, null.null);
Copy the code
  • “Proceed” is a process that calls “proceed” when the preceding node has been preprocessed, but “proceed” is not finished. It waits until the next node has been preprocessed in turn and “proceed” is finished. Then the code that follows “proceed” is a bit like a recursion.

  • So RetryAndFollowUpInterceptor all proceed before this line is front, proceed the rear work after, and proceed the role of this line is to put the job to the next node, and to wait.
  • Once this is understood, the working structure of the OKHTTP chain is clear.
  • If you look at his post job, what’s his post job? If you think about it, try again, for example, the following one, RouteException, that means you failed to find the IP address, what do you do? If you have multiple IP addresses, use another IP. But he also has a premise, click in to see the recover.
catch (RouteException e) {
  // The attempt to connect via a route failed. The request will not have been sent.
  if(! recover(e.getLastConnectException(), streamAllocation,false, request)) {
    throw e.getLastConnectException();
  }
Copy the code
  • He’s going to look, first, to see if you have this configuration retryOnConnectionFailure(), if the connection fails, if there’s a retry, and then hasMoreRoutes(), if there’s another route.
  • Anyway, this is to see if you can try to recover from the situation.
private boolean recover(IOException e, StreamAllocation streamAllocation,
    boolean requestSendStarted, Request userRequest) {
  streamAllocation.streamFailed(e);

  // The application layer has forbidden retries.
  if(! client.retryOnConnectionFailure())return false;

  // We can't send the request body again.
  if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;

  // This exception is fatal.
  if(! isRecoverable(e, requestSendStarted))return false;

  // No more routes to attempt.
  if(! streamAllocation.hasMoreRoutes())return false;

  // For failure recovery, use the same route selector with a new connection.
  return true;
}
Copy the code
  • The rest of the postposition work is similar to this, which is the judgment of the exception.
  • You’re gonna end up here,
Request followUp = followUpRequest(response, streamAllocation.route());
Copy the code
  • You go get a copy of your followUpRequest, and if your request is successful this will return empty,
if (followUp == null) {
  if(! forWebSocket) { streamAllocation.release(); }return response;
}
Copy the code
  • If you fail and meet the retry criteria, or jump criteria, you get a new followUpRequest, a new request assigned to it, and then loop.
request = followUp;
Copy the code
  • If you look up, it’s an infinite loop.
  • Either you get the result, return, the request was successful;
  • Or you get 301, 302, you give him a new request, and you cycle again,
while (true) {... }Copy the code
  • Interceptor is mainly a post logic.

2.BridgeInterceptor

  • The focus is also the Intercept method
public final class RetryAndFollowUpInterceptor implements Interceptor {...@Override public Response intercept(Chain chain) throws IOException {
  Request userRequest = chain.request();
  Request.Builder requestBuilder = userRequest.newBuilder();

  RequestBody body = userRequest.body();
  if(body ! =null) {
    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"); }}if (userRequest.header("Host") = =null) {
    requestBuilder.header("Host", hostHeader(userRequest.url(), false));
  }

  if (userRequest.header("Connection") = =null) {
    requestBuilder.header("Connection"."Keep-Alive");
  }

  // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
  // the transfer stream.
  boolean transparentGzip = false;
  if (userRequest.header("Accept-Encoding") = =null && userRequest.header("Range") = =null) {
    transparentGzip = true;
    requestBuilder.header("Accept-Encoding"."gzip");
  }

  List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
  if(! cookies.isEmpty()) { requestBuilder.header("Cookie", cookieHeader(cookies));
  }

  if (userRequest.header("User-Agent") = =null) {
    requestBuilder.header("User-Agent", Version.userAgent());
  }

  Response networkResponse = chain.proceed(requestBuilder.build());

  HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

  Response.Builder responseBuilder = networkResponse.newBuilder()
      .request(userRequest);

  if (transparentGzip
      && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
      && HttpHeaders.hasBody(networkResponse)) {
    GzipSource responseBody = new GzipSource(networkResponse.body().source());
    Headers strippedHeaders = networkResponse.headers().newBuilder()
        .removeAll("Content-Encoding")
        .removeAll("Content-Length")
        .build();
    responseBuilder.headers(strippedHeaders);
    String contentType = networkResponse.header("Content-Type");
    responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
  }

  returnresponseBuilder.build(); }... }Copy the code
  • You find the proceed method, and you use that to determine what’s ahead and what’s behind.
  • So let’s see what the front does.
if(contentType ! =null) {
  requestBuilder.header("Content-Type", contentType.toString());
}
Copy the code
  • If you have a body, add a content-type to it.
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");
}
Copy the code
  • And then content-length, you didn’t know how long your Content was before, you didn’t know how long the body was during creation, now you know, you can write its Length. You can write it in Content-length, you can write it in transfer-encoding, you can write it either way, and it will automatically decide which one it should write. Content-length is fixed Length, transfer-encoding is variable Length.
if (userRequest.header("Host") = =null) {
  requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
Copy the code
  • Example Add host, host name.
if (userRequest.header("Accept-Encoding") = =null && userRequest.header("Range") = =null) {
  transparentGzip = true;
  requestBuilder.header("Accept-Encoding"."gzip");
}
Copy the code
  • What is accept-encoding? The encoding format you accept.
  • And a GZIP will be added. If you do not add it, it means that I, as a client, can accept you to send me a gZIP compressed encoding format of data. It’s interesting, I haven’t said as a developer whether I’ll accept it or not, but you’ve accepted it for me. What if I can’t handle it? Why do you do that? Because this thing okHTTP he made for me. Okhttp has support for this data format itself. So he will proactively add this encoding. You don’t have to as a software developer. The server sent gZIP data, I was responsible for decompressing the data, so as to save bandwidth. All servers, as long as he supports, then you use HTTP, he must help you compress data. The way to decompress is in the following post – work.
if (transparentGzip
    && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
    && HttpHeaders.hasBody(networkResponse)) {
  GzipSource responseBody = new GzipSource(networkResponse.body().source());
  Headers strippedHeaders = networkResponse.headers().newBuilder()
      .removeAll("Content-Encoding")
      .removeAll("Content-Length")
      .build();
  responseBuilder.headers(strippedHeaders);
  String contentType = networkResponse.header("Content-Type");
  responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
Copy the code
  • On the other hand, you have to write the cookieJar yourself.
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if(! cookies.isEmpty()) { requestBuilder.header("Cookie", cookieHeader(cookies));
}
Copy the code
  • A quick word about the CookieJar
public interface CookieJar {
  /** A cookie jar that never accepts any cookies. */
  CookieJar NO_COOKIES = new CookieJar() {
    @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {}@Override public List<Cookie> loadForRequest(HttpUrl url) {
      returnCollections.emptyList(); }}; . }Copy the code
  • Just two ways:
    • SaveFromResponse: Get the response and save it.
    • LoadForRequest: When I need to make a request, I pull it out of my cookieJAR. Did he ever save the domain name I correspond to.
  • How to write? I just have to specify what this thing is.
    • You see the default event is this, right? I won’t write it like that.
    • I have a Map, how to save, cookies. Put
CookieJar NO_COOKIES = new CookieJar() {
    Map map = new HashMap();
  @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
      map.put(url,cookies)
  }

  @Override public List<Cookie> loadForRequest(HttpUrl url) {
    returncookies.get(url); }};Copy the code
  • So this is the CookieJAR, and you have to implement it yourself. The default is not implemented. By default, both store and write empty code.
if (userRequest.header("User-Agent") = =null) {
  requestBuilder.header("User-Agent", Version.userAgent());
}
Copy the code
  • And then the user agent, if you don’t set the user agent, it adds a string.
public static String userAgent(a) {
  return "Okhttp / 3.10.0";
}
Copy the code
  • Next he’ll do proceed,
Response networkResponse = chain.proceed(requestBuilder.build());
Copy the code
  • Proceed then does the decoding, the parsing of the data.
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

Response.Builder responseBuilder = networkResponse.newBuilder()
    .request(userRequest);

if (transparentGzip
    && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
    && HttpHeaders.hasBody(networkResponse)) {
  GzipSource responseBody = new GzipSource(networkResponse.body().source());
  Headers strippedHeaders = networkResponse.headers().newBuilder()
      .removeAll("Content-Encoding")
      .removeAll("Content-Length")
      .build();
  responseBuilder.headers(strippedHeaders);
  String contentType = networkResponse.header("Content-Type");
  responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
Copy the code
  • So what is BridgeInterceptor?
    • Just before the launch, I get the data ready for the launch, and I compress it and I provide support. So what happens is you solve all of these things, and then you compress them.

3.CacheInterceptor

  • The CacheInterceptor is very simple.
  • His Intercept is a treatment of cash, saving cash. The key to storing cash is this row.
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Copy the code
  • Click in to have a look.
public Factory(long nowMillis, Request request, Response cacheResponse) {
  this.nowMillis = nowMillis;
  this.request = request;
  this.cacheResponse = cacheResponse;

  if(cacheResponse ! =null) {
    this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
    this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
    Headers headers = cacheResponse.headers();
    for (int i = 0, size = headers.size(); i < size; i++) {
      String fieldName = headers.name(i);
      String value = headers.value(i);
      if ("Date".equalsIgnoreCase(fieldName)) {
        servedDate = HttpDate.parse(value);
        servedDateString = value;
      } else if ("Expires".equalsIgnoreCase(fieldName)) {
        expires = HttpDate.parse(value);
      } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
        lastModified = HttpDate.parse(value);
        lastModifiedString = value;
      } else if ("ETag".equalsIgnoreCase(fieldName)) {
        etag = value;
      } else if ("Age".equalsIgnoreCase(fieldName)) {
        ageSeconds = HttpHeaders.parseSeconds(value, -1); }}}}Copy the code
  • According to the cash you received, the current date, your data, etc., to determine whether your cash expired, if not expired, directly return the data.
if (networkRequest == null) {
  return cacheResponse.newBuilder()
      .cacheResponse(stripBody(cacheResponse))
      .build();
}
Copy the code
  • If the data is not expired, a fake response is created and returned.
  • If the third one is a CacheInterceptor, its cash does not expire, and it simply returns, leaving nothing behind. There’s no network interaction at all, that’s what cash does, and it doesn’t matter, that’s what HTTP expects. It was bypassed, but it didn’t cost anything. This is also the benefit of Intercept. Your nodes are transparent to the front and back of you. You are only responsible for your own business. The previous node does not need to know whether this information is coming from cash or a network request.

  • There’s nothing to tell, the code is still complicated, but we know where the core is.

4.ConnectInterceptor

  • ConnectInterceptor is not an interceptor we can read, at least not without taking time to read it. But the general structure should be understood.
  • This is where he interacts with HTTPS and SSL. The key is these two lines.
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
Copy the code
  • There’s a line that says newStream, what is Steam?

    • It’s this interaction between you and the web. Create a newStream and return an HttpCodec object.
  • So what is a Codec?

    • C) Codec D) code D) decode Decoding what code? Not with music videos, but with data from our network.
    • His interface has two implementations. Why two? One is http1 and the other is http2, http2 is in binary form, while 1, 1.0, 1.1, 1.9 are all in text form. So they should encode and decode completely two sets of algorithms.
  • The codec that your newStream creates is just one of them. It also creates a connection. It creates a connection that is not created on the line below. The core is actually a single line of code.

  • NewStream, let’s jump in and take a look. We’re going to jump a couple of times, so we don’t need to know, just know what we’re talking about. I want you to know how he interacts with TCP, how he interacts with SSL. So let’s keep looking. There’s a findHealthyConnection in newStream.

RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
    writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
Copy the code
  • He will try to get a healthy connection, one that is available now. And then click in, and he has a findConnection,
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
    pingIntervalMillis, connectionRetryEnabled);
Copy the code
  • Find him first, then make sure he’s healthy. There is another connect inside findConnection
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
    connectionRetryEnabled, call, eventListener);
Copy the code
  • And then there’s the connectSocket, the thing that connects to TCP is called a socket.
connectSocket(connectTimeout, readTimeout, call, eventListener);
Copy the code
  • ConnectStart, I’m starting to connect, and then connectSocket, and that’s where the connection is set up. And then we’ll have a buffer, and we’ll talk about buffer when we say IO.
  • This is what connects to the socket. So this is where you get the socket. And your socket is connected to the other party. We don’t need to know how he connected.

Anyway, you can see here, okHTTP, he’s making a TCP connection. This is completely different from the HTTP libraries we used a few years ago, they all used either Apache’s HttpClient or Android’s built-in HttpUrlConnection. None of them make their own connections, but okHTTP does.

eventListener.connectStart(call, route.socketAddress(), proxy);
rawSocket.setSoTimeout(readTimeout);
try {
  Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
  ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
  ce.initCause(e);
  throw ce;
}

The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0
// More details:
// https://github.com/square/okhttp/issues/3245
// https://android-review.googlesource.com/#/c/271775/
try {
  source = Okio.buffer(Okio.source(rawSocket));
  sink = Okio.buffer(Okio.sink(rawSocket));
} catch (NullPointerException npe) {
  if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
    throw newIOException(npe); }}Copy the code
  • Then turn back and there’s an establishProtocol under connectSocket
} else {
  connectSocket(connectTimeout, readTimeout, call, eventListener);
}
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
Copy the code
  • Protocol is HTTP, http1.1 and all that stuff. Click on that
connectTls(connectionSpecSelector);
Copy the code
  • This is where the HTTPS TLS connection is established. Click on it again.
  • SSLSocketFactory and all of that stuff, the ConnectionSpec, all of that stuff that we’ve set up before, is all used here.
  • Eventually Handshak, whatever hostnameVerifier is used, is used here to establish an SSL connection, or TLS connection.
sslSocket.startHandshake();
Copy the code
  • Anyway, in the ConnectInterceptor, what he does, let’s just say a little bit, is establish the connection. Establish a TCP connection or add a TLS connection to a TCP connection.

He doesn’t have a post job.

5.CallServerInterceptor

  • The CallServerInterceptor also gives you a cursory look at what the code does.
  • He’s the last one, he doesn’t need proceed. He finishes his work and returns.
  • He has done some substantial work, and we only need to look at a few points to know what substantial work is.

First of all, look at this writeRequestHeaders,

httpCodec.writeRequestHeaders(request);
Copy the code
  • WriteRequestHeaders is what httpCodec does, the codec, what does the codec do? He’s talking directly to the socket, so what’s the meaning of the headers that write requests to the socket? Point in. Is an interface, look at his implementation, HTTP1 and 2 can be. Go down to writeRequest.
public void writeRequest(Headers headers, String requestLine) throws IOException {
  if(state ! = STATE_IDLE)throw new IllegalStateException("state: " + state);
  sink.writeUtf8(requestLine).writeUtf8("\r\n");
  for (int i = 0, size = headers.size(); i < size; i++) {
    sink.writeUtf8(headers.name(i))
        .writeUtf8(":")
        .writeUtf8(headers.value(i))
        .writeUtf8("\r\n");
  }
  sink.writeUtf8("\r\n");
  state = STATE_OPEN_REQUEST_BODY;
}
Copy the code
  • You may not know what sink means because it’s an okio thing, not a Java IO thing, but you can tell by the name: writeUtf8, write a string of utf8. Write requestLine, your requestLine. Write to the socket, that is, write to the network, write to your TCP or TLS port, and eventually write to the other side of the network. Okhttp is a direct operation of the network. He makes connections. He communicates. After writing this, write a newline writeUtf8(“\r\n”).
  • And then you write out the name of each header, you put a colon, you write down the value, and then you wrap it.
  • And then I’m going to write body.
  • It’s just the HTTP format, and it’s just implemented that way.

When you get back to the CallServerInterceptor, after you’ve written the request, it’s going to read, read what’s in the response.

responseBuilder = httpCodec.readResponseHeaders(false);
Copy the code
  • When you’ve read it, put it in the responseBody. And then return response.
response = response.newBuilder()
    .body(httpCodec.openResponseBody(response))
    .build();
Copy the code
  • When it returns, it does the other node’s postset method.

  • ConnectInterceptor ends by returning,
  • ConnectInterceptor works after it, it doesn’t work after it, then
  • CacheInterceptor, when he gets it, he tries to save the cash, and when he’s done
  • BridgeInterceptor, he will do the decompression of GZIP, as well as the interpretation of various data, and then
  • RetryAndFollowUpInterceptor, this isn’t 301302 or the other what he would like me to jump, anyhow network will not in the jump,

The network problem will come back when the ConnectInterceptor fails, not when the ConnectInterceptor fails.

  • If something goes wrong and he needs to try again, he goes like this.

6. Interceptors, networkInterceptors

  • And finally what are these two
interceptors.addAll(client.interceptors());
interceptors.addAll(client.networkInterceptors());
Copy the code
  • Their implementation is the same as any other interceptor, for example, if I want to add an interceptor, but do nothing.
OkHttpClient client =new OkHttpClient.Builder()
        .certificatePinner(certificatePinner)
        .addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                // Pre-work
                Response response = chain.proceed(chain.request());
                // Post work
                return response;
            }
        })
        .build();
Copy the code

It’s like I put a middleman in the middle who didn’t do anything. If I want to do something, I can insert the pre and post work.

  • What’s the difference between interceptors and networkInterceptors?

    • It’s all about location,
    • Interceptors occur at the beginning,
    • NetworkInterceptors occurs at the end, and the returned data may not be readable, since the data has not been decomcompressed. Therefore, networkInterceptors are generally related to the network, related to writing data, do some work on the data front and back, you want to operate this time to check the Internet, the general situation is not to use him.
  • Okhttp What is his role? Compared to Retrofit?

    • It’s a process that goes from connection establishment, to TLS connection establishment, to HTTP transport, to support for various HTTP features such as retries, hops, cash, cookies. He took over the entire HTTP job, and incidentally made the API a little more comfortable than native.

Conclusion figure