/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okhttp3.internal.huc; import java.io.IOException; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.ProtocolException; import java.net.SocketTimeoutException; import okhttp3.MediaType; import okhttp3.Request; import okhttp3.RequestBody; import okio.BufferedSink; import okio.Timeout; /** * A request body that's populated by blocking writes to an output stream. The output data is either * fully buffered (with {@link BufferedRequestBody}) or streamed (with {@link StreamedRequestBody}). * In either case the bytes of the body aren't known until the caller writes them to the output * stream. */ abstract class OutputStreamRequestBody extends RequestBody { private Timeout timeout; private long expectedContentLength; private OutputStream outputStream; boolean closed; protected void initOutputStream(final BufferedSink sink, final long expectedContentLength) { this.timeout = sink.timeout(); this.expectedContentLength = expectedContentLength; // An output stream that writes to sink. If expectedContentLength is not -1, then this expects // exactly that many bytes to be written. this.outputStream = new OutputStream() { private long bytesReceived; @Override public void write(int b) throws IOException { write(new byte[] {(byte) b}, 0, 1); } @Override public void write(byte[] source, int offset, int byteCount) throws IOException { if (closed) throw new IOException("closed"); // Not IllegalStateException! if (expectedContentLength != -1L && bytesReceived + byteCount > expectedContentLength) { throw new ProtocolException("expected " + expectedContentLength + " bytes but received " + bytesReceived + byteCount); } bytesReceived += byteCount; try { sink.write(source, offset, byteCount); } catch (InterruptedIOException e) { throw new SocketTimeoutException(e.getMessage()); } } @Override public void flush() throws IOException { if (closed) return; // Weird, but consistent with historical behavior. sink.flush(); } @Override public void close() throws IOException { closed = true; if (expectedContentLength != -1L && bytesReceived < expectedContentLength) { throw new ProtocolException("expected " + expectedContentLength + " bytes but received " + bytesReceived); } sink.close(); } }; } public final OutputStream outputStream() { return outputStream; } public final Timeout timeout() { return timeout; } public final boolean isClosed() { return closed; } @Override public long contentLength() throws IOException { return expectedContentLength; } @Override public final MediaType contentType() { return null; // Let the caller provide this in a regular header. } public Request prepareToSendRequest(Request request) throws IOException { return request; } }