Android Interview list----okhttp source code Analysis (iii)

Source: Internet
Author: User
Tags flush getdate int size throw exception
SendRequest ()

In the last article we talked about the SendRequest () method, which goes on to look at the Readresponse method:

/** from Httpengine class */public void Readresponse () throws IOException {if (This.userresponse = null) {if (th Is.networkrequest = = NULL && This.cacheresponse = null) {throw new IllegalStateException ("Call Sendr Equest () first! ");}
            else if (this.networkrequest!= null) {Response networkresponse;
               if (this.forwebsocket) {this.httpStream.writeRequestHeaders (this.networkrequest);
            Networkresponse = This.readnetworkresponse (); 
               else if (!this.callerwritesrequestbody) {//First execute interceptor, then write request to Httpstream Sinkbuffer, finally send buffer and read response Networkresponse = (new Httpengine.networkinterceptorchain (0, This.networkrequest)). Proceed (This.networkReq
            Uest); 
               else {if (this.bufferedrequestbody!= null && this.bufferedRequestBody.buffer (). Size () > 0L) {
              Will request the body of the buffer sent out, so requestbodyout in the body    This.bufferedRequestBody.emit ();  } if (This.sentrequestmillis = = -1l) {if okheaders.contentlength (this.networkrequest) = = -1l && this.requestbodyout instanceof retryablesink) {Long Responsecache = ((Retryablesink)
                     this.requestbodyout). ContentLength (); This.networkrequest = This.networkRequest.newBuilder (). Header ("Content-length", Long.tostring (Responsecache)).
                  Build ();
               } this.httpStream.writeRequestHeaders (This.networkrequest);
                     } if (this.requestbodyout!= null) {if (this.bufferedrequestbody!= null) {
                  This.bufferedRequestBody.close ();
                  else {this.requestBodyOut.close ();   
                                } if (This.requestbodyout instanceof Retryablesink) {//body is written to the socket This.httpstreaM.writerequestbody ((Retryablesink) this.requestbodyout);
            } networkresponse = This.readnetworkresponse ();
            } this.receiveheaders (Networkresponse.headers ()); if (this.cacheresponse!= null) {if (Validate (This.cacheresponse, networkresponse)) {This . Userresponse = This.cacheResponse.newBuilder (). Request (This.userrequest). Priorresponse (Stripbody ( This.priorresponse)). Headers (Combine (This.cacheResponse.headers (), Networkresponse.headers ())). Cacheresponse (
                  Stripbody (This.cacheresponse)). Networkresponse (Stripbody (Networkresponse)). Build ();
                  Networkresponse.body (). Close ();
                  This.releasestreamallocation ();
                  Internalcache responseCache1 = Internal.instance.internalCache (this.client);
                  Responsecache1.trackconditionalcachehit (); Responsecache1.update (This.cacheresponse, Stripbody (this.userresponse));
                  This.userresponse = This.unzip (this.userresponse);
               Return
            } util.closequietly (This.cacheResponse.body ()); This.userresponse = Networkresponse.newbuilder (). Request (This.userrequest). Priorresponse (Stripbody (this.pri
            Orresponse)). Cacheresponse (Stripbody (This.cacheresponse)). Networkresponse (Stripbody (Networkresponse)). Build ();
               if (Hasbody (This.userresponse)) {This.maybecache ();
            This.userresponse = This.unzip (This.cachewritingresponse (This.storerequest, this.userresponse)); }

         }
      }
   }

Navigate to Line 8th:

This.httpStream.writeRequestHeaders (this.networkrequest);

Writerequestheaders This method is a method of httpstream interfaces, rewritten by Http1xstream and Http2xstream.

If the http1.x protocol is used, the Writerequestheaders method inside the Http1xstream is executed, and if it is http2.0, the Http2xstream is executed. We know from the above that this depends on whether the request is HTTP or HTTPS. We use 1x as the class:

/** from Http1xstream class *

/public void Writerequestheaders (Request request) throws IOException {
        This.httpEngine.writingRequestHeaders ();
        String requestline = requestline.get (Request, This.httpengine
                . getconnection (). Route (). Proxy (). Type ());
        This.writerequest (Request.headers (), requestline);
    }

First get the requested Requestline (Statusline), which is a concatenation of the method name and Url,http protocol.
Second, execute the Writerequest method:

/** from Http1xstream class *

/public void Writerequest (Headers Headers, String requestline)
            throws IOException {
        if (this.state!= 0) {
            throw new IllegalStateException ("state:" + this.state);
        } else {
            This.sink.writeUtf8 (requestline). WriteUtf8 ("\ r \ n");
            int i = 0;

            for (int size = Headers.size (); i < size; ++i) {
                This.sink.writeUtf8 (headers.name (i)). WriteUtf8 (":")
                        . WriteUtf8 (Headers.value (i)). WriteUtf8 ("\ r \ n");

            This.sink.writeUtf8 ("\ r \ n");
            This.state = 1;
        }
    }

The main thing is to write the Statusline and header information to Sink,sink, because from the last article we know that the bottom of okhttp is socket communication, So sink is the equivalent of the inputstream we use in HttpURLConnection, which is the write stream of the socket, and source is outputstream. Readnetworkresponse ()

Look at line 9th again:

/** from Httpengine class * *

private Response Readnetworkresponse () throws IOException {
     //sink operations
      on flush stream This.httpStream.finishRequest ();
      Wait for the server to correspond and read the server return information assembled into the Response Response we need
      Networkresponse = This.httpStream.readResponseHeaders (). Request ( this.networkrequest). Handshake (This.streamAllocation.connection (). Handshake ()). Header (Okheaders.sent_millis, Long.tostring (This.sentrequestmillis)). Header (Okheaders.received_millis, long.tostring (system.currenttimemillis ()). build ();
      if (!this.forwebsocket) {
         networkresponse = Networkresponse.newbuilder (). Body (this.httpStream.openResponseBody (Networkresponse)). Build ();
      }
      if ("Close". Equalsignorecase (Networkresponse.request (). Header ("Connection")) | | ' Close '. Equalsignorecase (Networkresponse.header ("Connection"))) {
         this.streamAllocation.noNewStreams ();
      }

      return networkresponse;
   }

See how the 7th line of this method is assembled.

/** from the Http1xstream class (two methods below)/public Builder readresponseheaders () throws IOException {return this.readresponse
    ();
            Public Builder Readresponse () throws IOException {if (this.state!= 1 && this.state!= 3) {
        throw new IllegalStateException ("state:" + this.state);
                else {try {statusline E;
                 Builder Exception1;
                If return code is not 100, returning the response object directly to 100,continue,server will continue to return response string, we need to continue receiving and parsing in the while loop
                     do {e = Statusline.parse (This.source.readUtf8LineStrict ());
                            Reads the reply from the input stream and assembles it into a reply message, which is placed in the constructed response factory class Build Exception1 = (new Builder ()). Protocol (E.PROTOCOL) . Code (E.CODE). Message (E.message)//reply header read. h
                Eaders (This.readheaders ());

                while (E.code = 100); This.state = 4;
            return exception1;  catch (Eofexception arg2) {IOException exception = new IOException ("unexpected
                End of stream on "+ this.streamallocation);
                Exception.initcause (ARG2);
            Throw exception;
 }
        }
    }

The above is mainly to reply to the head of the information to organize, and the Readnetworkresponse method of the 13th line is mainly the return of the body to assemble and organize:

/** from the Http1xstream class (two methods below)/public

responsebody openresponsebody (Response Response) throws IOException {
        SOURCE Source = This.gettransferstream (response);
        return new Realresponsebody (Response.headers (), Okio.buffer (source));

    Private Source Gettransferstream (Response Response) throws IOException {
        if (! Httpengine.hasbody (response)) {return
            this.newfixedlengthsource (0L);
        } else if ("chunked". Equalsignorecase (Response
                . Header ("transfer-encoding")) {return
            this.newchunkedsource (this.httpengine);
        } else {
            Long contentlength = okheaders.contentlength (response);
            return contentlength!= -1l? This
                    . Newfixedlengthsource (contentlength): this
                    . Newunknownlengthsource ();
        }
    }
This.newchunkedsource (This.httpengine);

This code finally executes to the Read method of Chunkedsource (implementing the Source interface):

    Public long read (Buffer sink, long ByteCount) throws IOException {if (ByteCount < 0L) {
            throw new IllegalArgumentException ("ByteCount < 0:" + byteCount);
            else if (this.closed) {throw new IllegalStateException ("closed");
            else if (!this.hasmorechunks) {return-1l; else {if (This.bytesremaininginchunk = 0L | | this.bytesremaininginchunk = = -1L
                    ) {this.readchunksize ();
                    if (!this.hasmorechunks) {return-1l; } Long Read = Http1xStream.this.source.read (sink, Math.min (byte
                Count, This.bytesremaininginchunk));
                    if (read = = -1l) {this.endofinput (false);
             throw new Protocolexception ("Unexpected End of Stream");   else {this.bytesremaininginchunk = read;
                return read; }
            }
        }

Above we have analyzed that source is the input stream we read from the service, and similar to the Outputstream,read method is read from the service.

Finally, back to the 9th line of the Readresponse method, we got the complete networkresponse.

Let's see how the Validate (Cacheresponse, Networkresponse) method determines whether the cache is available:

/** Readresponse method from the Httpengine class * *

  private static Boolean validate (Response cached, Response network) {
    // If the server returns 304, cache valid
      if (network.code () = 304) {return
         true;
      } else {
         Date lastmodified = Cached.headers (). GetDate ("last-modified");
            The cache is valid if
         (lastmodified!= null) {
            Date networklastmodified = by last-modified in the cache and network request response to calculate whether it is the most recent data. Network.headers (). GetDate ("last-modified");
            if (networklastmodified!= null && networklastmodified.gettime () < Lastmodified.gettime ()) {return
               true;
            }

         return false;
      }
   }

Cache response exist in the case, should be cached expired or forced to discard the cache, in this case, the caching policy to the server to judge, the client only to send conditional GET request to verify the contents of the cache can be changed, if the cache is valid, then return 304 not Modifiled, and response will not contain body, otherwise cache changes, reply to, OK. The response contains the body. There are two ways to conditional get requests, one is last-modified-date, the other is ETag. Last-modified-date is used here to calculate whether it is the most recent data through caching and the last-modified in the network request response, and if so, the cache is valid.

Back to the first article find two parameters Forwebsocket and callerwritesrequestbody, you can find that both parameters are false, so that is to say
In the Readresponse method, the default is not to do the 8th, 9 lines, but will go to the 11th line, we analyzed the request to send the Interceptor mode used, where the answer to the operation is also in the same way, different from the request call is intercept, here with the proceed. So we need to analyze the following interceptor,
The focus is on the proceed approach:

/** from Httpengine internal class Networkinterceptorchain implements the chain interface/public Response proceed (Request request) throws IOException {
         ++this.calls; if (This.index > 0) {Interceptor response = (Interceptor) HttpEngine.this.client.networkInterceptors (). Get (t
            HIS.INDEX-1);
            Address code = this.connection (). Route (). address (); if (!request.url (). Host (). Equals (Code.url (). Host ()) | | Request.url (). Port ()!= Code.url (). Port ()) {throw NE
            W illegalstateexception ("Network Interceptor" + response + "must retain the same host and Port"); } if (This.calls > 1) {throw new IllegalStateException ("Network Interceptor" + Response +
            "Must call proceed () exactly once"); } if (This.index < HttpEngine.this.client.networkInterceptors (). Size ()) {//interceptors are removed according to the number of interceptors and hold Line intercept inside the user-defined processing mode, and we have analyzed before//httpengine.networkinterceptorchain ARG6 = HtTpEngine.this.new Networkinterceptorchain (This.index + 1, request);
            Interceptor Arg9 = (Interceptor) HttpEngine.this.client.networkInterceptors (). get (This.index);
            Response interceptedresponse = arg9.intercept (ARG6);  if (arg6.calls!= 1) {throw new IllegalStateException ("Network Interceptor" + ARG9 + "must call proceed ()
            Exactly once "); else if (Interceptedresponse = null) {throw new NullPointerException ("Network Interceptor" + ARG9 + "R
            eturned null ");
            else {return interceptedresponse;
            } else {//write requests Headers HttpEngine.this.httpStream.writeRequestHeaders (request);
             HttpEngine.this.networkRequest = Request; Write some request body Sink Arg4 = HttpEngine.this.httpStream.createRequestBody (Request, Request.body (). ContentLength ())
               ;
               Bufferedsink Arg7 = Okio.buffer (ARG4); Request.body(). WriteTo (ARG7);
            Arg7.close ();
            Flush the previously written data to the socket and read the server reply Response arg5 = HttpEngine.this.readNetworkResponse ();
            int Arg8 = Arg5.code (); if ((Arg8 = = 204 | | arg8 = = 205) && arg5.body () contentlength () > 0L) {throw new Protocolexcepti
            On ("HTTP" + Arg8 + "had Non-zero content-length:" + arg5.body (). ContentLength ());
            else {return arg5; }
         }
      }

We have analyzed the Writerequestheader method and the Readnetworkresponse method inside. After so many 7788 of jumps, nesting, finally got the response we need. Finally back to our first article in the original GetResponse method, find these two sentences:

            Response arg22 = This.engine.getResponse ();
            Request arg23 = This.engine.followUpRequest ();

The first sentence is obviously to get our response and return directly to Userresponse.
The second sentence is the process of redirecting the result of the request, and after the client sends a request, the server may revert to a redirected response and in this response inform the client of the IP of the server that needs to be accessed again. At this point, the client needs to resend the request to the new server and wait for a reply from the new server. So we need to individually Judge redirect response and send multiple request. With Okhttp, you don't have to worry about it, it will automatically help you accomplish all of this. The Followuprequest () method in Okhttp is to complete this function. Summary

Okhttp the underlying source is still quite complex, after all, its function is so powerful. Okhttp defaults to keep-alive persistent connection technology (does not represent a long connection, depending on the server) and supports GZIP-encoded response. In the cache processing, if the cache is available, then use the cache directly, otherwise use network data. Okhttp will do the cache expiration of the judgment and after the expiration of the verification. With Okhttp, all this you do not have to control, it helps you cover off.

When user authentication and redirection is required, we generally need to send the authentication request, or send the request to the new server, which is to regenerate the new request and send it. With Okhttp, all this you do not care, it helped you cover off. Late

1, read several times to update the errors and deficiencies in the point.
2, improve the flow chart, to achieve a comprehensive understanding.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.