接着上一章分析request.body().writeTo();
public @Nullable RequestBody body() {
return body;
}
public abstract class RequestBody {
............
/** Returns a new request body that transmits the content of {@code file}. */
public static RequestBody create(final @Nullable MediaType contentType, final File file) {
if (file == null) throw new NullPointerException("content == null");
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return file.length();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
sink.writeAll(source);
} finally {
Util.closeQuietly(source);
}
}
};
}
}
在ConnectInterceptor中查看httpCodec是如何创建的,RealConnection
public HttpCodec newCodec(OkHttpClient client, Interceptor.Chain chain,
StreamAllocation streamAllocation) throws SocketException {
if (http2Connection != null) {
return new Http2Codec(client, chain, streamAllocation, http2Connection);
} else {
socket.setSoTimeout(chain.readTimeoutMillis());
source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
// sink初始化 sink = Okio.buffer(Okio.sink(socket));
sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
return new Http1Codec(client, streamAllocation, source, sink);
}
}
1.Http2请求创建过程:Http2codec createRequestBody():
public Http2Codec(OkHttpClient client, Interceptor.Chain chain, StreamAllocation streamAllocation,
Http2Connection connection) {
this.client = client;
this.chain = chain;
this.streamAllocation = streamAllocation;
this.connection = connection;
protocol = client.protocols().contains(Protocol.H2C) ? Protocol.H2C : Protocol.HTTP_2;
}
@Override public Sink createRequestBody(Request request, long contentLength) {
return stream.getSink();
}
@Override public void writeRequestHeaders(Request request) throws IOException {
if (stream != null) return;
boolean hasRequestBody = request.body() != null;
List<Header> requestHeaders = http2HeadersList(request);
//初始化stream成员变量,Http2Connection connection
stream = connection.newStream(requestHeaders, hasRequestBody);
stream.readTimeout().timeout(chain.readTimeoutMillis(), TimeUnit.MILLISECONDS);
stream.writeTimeout().timeout(chain.writeTimeoutMillis(), TimeUnit.MILLISECONDS);
}
Http2Connection类:
public Http2Stream newStream(List<Header> requestHeaders, boolean out) throws IOException {
return newStream(0, requestHeaders, out);
}
private Http2Stream newStream(
int associatedStreamId, List<Header> requestHeaders, boolean out) throws IOException {
boolean outFinished = !out;
boolean inFinished = false;
boolean flushHeaders;
Http2Stream stream;
int streamId;
synchronized (writer) {
synchronized (this) {
if (nextStreamId > Integer.MAX_VALUE / 2) {
shutdown(REFUSED_STREAM);
}
if (shutdown) {
throw new ConnectionShutdownException();
}
streamId = nextStreamId;
nextStreamId += 2;
stream = new Http2Stream(streamId, this, outFinished, inFinished, requestHeaders);
flushHeaders = !out || bytesLeftInWriteWindow == 0L || stream.bytesLeftInWriteWindow == 0L;
if (stream.isOpen()) {
streams.put(streamId, stream);
}
}
if (associatedStreamId == 0) {
writer.synStream(outFinished, streamId, associatedStreamId, requestHeaders);
} else if (client) {
throw new IllegalArgumentException("client streams shouldn't have associated stream IDs");
} else { // HTTP/2 has a PUSH_PROMISE frame.
writer.pushPromise(associatedStreamId, streamId, requestHeaders);
}
}
if (flushHeaders) {
writer.flush();
}
return stream;
}
Http2Stream类:
Http2Stream(int id, Http2Connection connection, boolean outFinished, boolean inFinished,
List<Header> requestHeaders) {
if (connection == null) throw new NullPointerException("connection == null");
if (requestHeaders == null) throw new NullPointerException("requestHeaders == null");
this.id = id;
this.connection = connection;
this.bytesLeftInWriteWindow =
connection.peerSettings.getInitialWindowSize();
this.source = new FramingSource(connection.okHttpSettings.getInitialWindowSize());
this.sink = new FramingSink();
this.source.finished = inFinished;
this.sink.finished = outFinished;
this.requestHeaders = requestHeaders;
}
......
public Sink getSink() {
synchronized (this) {
if (!hasResponseHeaders && !isLocallyInitiated()) {
throw new IllegalStateException("reply before requesting the sink");
}
}
return sink;
}
final class FramingSink implements Sink {
private static final long EMIT_BUFFER_SIZE = 16384;
/**
* Buffer of outgoing data. This batches writes of small writes into this sink as larges frames
* written to the outgoing connection. Batching saves the (small) framing overhead.
*/
private final Buffer sendBuffer = new Buffer();
boolean closed;
/**
* True if either side has cleanly shut down this stream. We shall send no more bytes.
*/
boolean finished;
@Override public void write(Buffer source, long byteCount) throws IOException {
assert (!Thread.holdsLock(Http2Stream.this));
sendBuffer.write(source, byteCount);
while (sendBuffer.size() >= EMIT_BUFFER_SIZE) {//循环写入
emitFrame(false);
}
}
/**
* Emit a single data frame to the connection. The frame's size be limited by this stream's
* write window. This method will block until the write window is nonempty.
*/
private void emitFrame(boolean outFinished) throws IOException {
long toWrite;
synchronized (Http2Stream.this) {
writeTimeout.enter();
try {
while (bytesLeftInWriteWindow <= 0 && !finished && !closed && errorCode == null) {
waitForIo(); // Wait until we receive a WINDOW_UPDATE for this stream.
}
} finally {
writeTimeout.exitAndThrowIfTimedOut();
}
checkOutNotClosed(); // Kick out if the stream was reset or closed while waiting.
toWrite = Math.min(bytesLeftInWriteWindow, sendBuffer.size());
bytesLeftInWriteWindow -= toWrite;
}
writeTimeout.enter();
try {
connection.writeData(id, outFinished && toWrite == sendBuffer.size(), sendBuffer, toWrite);
} finally {
writeTimeout.exitAndThrowIfTimedOut();
}
}
Http2Connection writeData():
public void writeData(int streamId, boolean outFinished, Buffer buffer, long byteCount)
throws IOException {
if (byteCount == 0) { // Empty data frames are not flow-controlled.
writer.data(outFinished, streamId, buffer, 0);//写入发送完成消息
return;
}
while (byteCount > 0) {
int toWrite;
synchronized (Http2Connection.this) {
try {
while (bytesLeftInWriteWindow <= 0) {
// Before blocking, confirm that the stream we're writing is still open. It's possible
// that the stream has since been closed (such as if this write timed out.)
if (!streams.containsKey(streamId)) {
throw new IOException("stream closed");
}
Http2Connection.this.wait(); // Wait until we receive a WINDOW_UPDATE.
}
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
toWrite = (int) Math.min(byteCount, bytesLeftInWriteWindow);
toWrite = Math.min(toWrite, writer.maxDataLength());
bytesLeftInWriteWindow -= toWrite;
}
byteCount -= toWrite;
//Http2Writer writer 在Http2Connection构造方法中 new=Http2Writer(builder.sink,client);
writer.data(outFinished && byteCount == 0, streamId, buffer, toWrite);
}
}
HttpWriter data()函数:
public synchronized void data(boolean outFinished, int streamId, Buffer source, int byteCount)
throws IOException {
if (closed) throw new IOException("closed");
byte flags = FLAG_NONE;
if (outFinished) flags |= FLAG_END_STREAM;
dataFrame(streamId, flags, source, byteCount);
}
void dataFrame(int streamId, byte flags, Buffer buffer, int byteCount) throws IOException {
byte type = TYPE_DATA;
frameHeader(streamId, byteCount, type, flags);
if (byteCount > 0) {
sink.write(buffer, byteCount);
}
}
sink变量是构造方法注入的,sink来自Http2Connection的构造方法,Http2Connection由RealConnection创建,经过Http2Codec->Http2Stream->Http2Connection传递过程。
private void startHttp2(int pingIntervalMillis) throws IOException {
socket.setSoTimeout(0); // HTTP/2 connection timeouts are set per-stream.
http2Connection = new Http2Connection.Builder(true)
.socket(socket, route.address().url().host(), source, sink)
.listener(this)
.pingIntervalMillis(pingIntervalMillis)
.build();
http2Connection.start();
}
以上方法在RealConnection的connet()函数调用,方法中的sink就是Http2Writer中的writer。sink在RealConnection的connectTls()函数或connectSocket()函数实例化的
..............
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 new IOException(npe);
}
}
...............
Okio.sink():
public static Sink sink(Socket socket) throws IOException {
if (socket == null) {
throw new IllegalArgumentException("socket == null");
} else if (socket.getOutputStream() == null) {
throw new IOException("socket's output stream == null");
} else {
AsyncTimeout timeout = timeout(socket);
Sink sink = sink((OutputStream)socket.getOutputStream(), (Timeout)timeout);
return timeout.sink(sink);
}
}
private static Sink sink(final OutputStream out, final Timeout timeout) {
if (out == null) {
throw new IllegalArgumentException("out == null");
} else if (timeout == null) {
throw new IllegalArgumentException("timeout == null");
} else {
return new Sink() {
public void write(Buffer source, long byteCount) throws IOException {
Util.checkOffsetAndCount(source.size, 0L, byteCount);
while(byteCount > 0L) {
timeout.throwIfReached();
Segment head = source.head;
int toCopy = (int)Math.min(byteCount, (long)(head.limit - head.pos));
out.write(head.data, head.pos, toCopy);
head.pos += toCopy;
byteCount -= (long)toCopy;
source.size -= (long)toCopy;
if (head.pos == head.limit) {
source.head = head.pop();
SegmentPool.recycle(head);
}
}
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
out.close();
}
public Timeout timeout() {
return timeout;
}
public String toString() {
return "sink(" + out + ")";
}
};
}
}
总结一下request.body().writeTo(Sink sink);的过程,writeTo函数中调用的sink.write(),其中的sink在CallServerInterceptor类intercept()函数中传入的,类型为RealBufferSink,父类为BufferSink,RealBufferSink中封装的CountingSink,CountingSink类中封装了FramingSink,在request.body().writeTo(Sink sink);的writeTo中调用的RealBufferSink的write方法,将要发送的数据写入成员变量buffer中,然后又调用了,RealBufferSink中的类型为CountingSink的成员变量sink的write()方法,将要发送的数据传给CountingSink,CountingSink的writer方法中调用了FramingSink的write()方法,FramingSink的write()方法复杂操作循环有调用了Http2Connection的writeData()方法,writeData()方法中调用了Http2Writer类的data()方法,data()方法中调用了sink,sink来自Http2Connection的构造方法,Http2Connetion在RealConnection中创建,sink是在RealConnection实例化的。
RealBufferSink封装了CountingSingk,CountingSink封装了FramingSink,FramingSink间接封装了Sink,像不像一个单向列表。
2.Http1请求创建过程:Http1Codec createRequestBody():
public final class Http1Codec implements HttpCodec {
private static final int STATE_IDLE = 0; // Idle connections are ready to write request headers.
private static final int STATE_OPEN_REQUEST_BODY = 1;
private static final int STATE_WRITING_REQUEST_BODY = 2;
private static final int STATE_READ_RESPONSE_HEADERS = 3;
private static final int STATE_OPEN_RESPONSE_BODY = 4;
private static final int STATE_READING_RESPONSE_BODY = 5;
private static final int STATE_CLOSED = 6;
private static final int HEADER_LIMIT = 256 * 1024;
/** The client that configures this stream. May be null for HTTPS proxy tunnels. */
final OkHttpClient client;
/** The stream allocation that owns this stream. May be null for HTTPS proxy tunnels. */
final StreamAllocation streamAllocation;
final BufferedSource source;
final BufferedSink sink;
int state = STATE_IDLE;
private long headerLimit = HEADER_LIMIT;
public Http1Codec(OkHttpClient client, StreamAllocation streamAllocation, BufferedSource source,
BufferedSink sink) {
this.client = client;
this.streamAllocation = streamAllocation;
this.source = source;
this.sink = sink;
}
@Override public Sink createRequestBody(Request request, long contentLength) {
//分块编码(chunked)。
if ("chunked".equalsIgnoreCase(request.header("Transfer-Encoding"))) {
// Stream a request body of unknown length.
return newChunkedSink();
}
//返回数据大小
if (contentLength != -1) {
// Stream a request body of a known length.
return newFixedLengthSink(contentLength);
}
throw new IllegalStateException(
"Cannot stream a request body without chunked encoding or a known content length!");
}
private final class ChunkedSink implements Sink {
private final ForwardingTimeout timeout = new ForwardingTimeout(sink.timeout());
private boolean closed;
ChunkedSink() {
}
@Override public Timeout timeout() {
return timeout;
}
@Override public void write(Buffer source, long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed");
if (byteCount == 0) return;
//sink在Http1Codec的构造方法中注入的
sink.writeHexadecimalUnsignedLong(byteCount);
sink.writeUtf8("\r\n");
sink.write(source, byteCount);
sink.writeUtf8("\r\n");
}
..................
private final class FixedLengthSink implements Sink {
private final ForwardingTimeout timeout = new ForwardingTimeout(sink.timeout());
private boolean closed;
private long bytesRemaining;
FixedLengthSink(long bytesRemaining) {
this.bytesRemaining = bytesRemaining;
}
@Override public Timeout timeout() {
return timeout;
}
@Override public void write(Buffer source, long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed");
checkOffsetAndCount(source.size(), 0, byteCount);
if (byteCount > bytesRemaining) {
throw new ProtocolException("expected " + bytesRemaining
+ " bytes but received " + byteCount);
}
sink.write(source, byteCount);
bytesRemaining -= byteCount;
}
@Override public void flush() throws IOException {
if (closed) return; // Don't throw; this stream might have been closed on the caller's behalf.
sink.flush();
}
@Override public void close() throws IOException {
if (closed) return;
closed = true;
if (bytesRemaining > 0) throw new ProtocolException("unexpected end of stream");
detachTimeout(timeout);
state = STATE_READ_RESPONSE_HEADERS;
}
}
除了创建sink的方式不一样以外和Http2一样的执行流程。