/** * Eagerly reads {@code byteCount} bytes from the source before launching a background task to * process the data. This avoids corrupting the stream. */ private void pushDataLater( final int streamId, final BufferedSource source, final int byteCount, final boolean inFinished) throws IOException { final Buffer buffer = new Buffer(); source.require(byteCount); // Eagerly read the frame before firing client thread. source.read(buffer, byteCount); if (buffer.size() != byteCount) throw new IOException(buffer.size() + " != " + byteCount); pushExecutor.execute( new NamedRunnable("OkHttp %s Push Data[%s]", hostname, streamId) { @Override public void execute() { try { boolean cancel = pushObserver.onData(streamId, buffer, byteCount, inFinished); if (cancel) frameWriter.rstStream(streamId, ErrorCode.CANCEL); if (cancel || inFinished) { synchronized (FramedConnection.this) { currentPushRequests.remove(streamId); } } } catch (IOException ignored) { } } }); }
@Override void writeBody(final BufferedSource in, final BufferedSink out) throws IOException { try { final ResponseBody data = body(); if (data != null) { final long length = data.contentLength(); if (length > 0) { out.write(data.source(), data.contentLength()); out.flush(); } else if (length < 0) { final Buffer buffer = new Buffer(); final long step = 65536; long read; while ((read = data.source().read(buffer, step)) > -1) { buffer.flush(); out.write(buffer, read); out.flush(); } } } } finally { // try { in.close(); } catch (final IOException ignore) {} // try { out.close(); } catch (final IOException ignore) {} // try { socket.close(); } catch (final IOException ignore) {} } }
public static void serializeAndCommitResponse(Response response, DiskLruCache.Editor edit) throws IOException { if (edit == null) { return; } boolean successful = false; try { new Entry(response).writeTo(edit); Buffer buffer = new Buffer(); ResponseBody body = response.body(); if (body != null) { body.source().read(buffer, body.contentLength()); } Sink sink = edit.newSink(RESPONSE_BODY); sink.write(buffer, buffer.size()); sink.close(); successful = true; } finally { if (successful) { commitQuitely(edit); } else { abortQuietly(edit); } } }
/** * Sets a response body built from the specified text. * * @param text the string value. * @param contentType the media type. * @return this */ public Builder body(final String text, final MediaType contentType) { if (text == null) return body((ResponseBody) null); final Buffer buffer = new Buffer().writeUtf8(text); this.mBody = new BufferResponse(contentType, buffer); contentLength(buffer.size()); contentType(contentType); return this; }
@Override public synchronized void onResponse(Response response) throws IOException { Buffer buffer = new Buffer(); Response.Body body = response.body(); body.source().readAll(buffer); responses.add(new RecordedResponse(response.request(), response, buffer.readUtf8(), null)); notifyAll(); }
@Override public InputStream getMessagePayload() throws IOException { if (request.body() == null) { return null; } Buffer buf = new Buffer(); request.body().writeTo(buf); return buf.inputStream(); }
private static String bodyAsString(RequestBody body) { try { Buffer buffer = new Buffer(); body.writeTo(buffer); return buffer.readString(body.contentType().charset()); } catch (IOException e) { throw new RuntimeException(e); } }
/** * Returns the concatenation of 8-bit, length prefixed protocol names. * http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4 */ static byte[] concatLengthPrefixed(List<Protocol> protocols) { Buffer result = new Buffer(); for (int i = 0, size = protocols.size(); i < size; i++) { Protocol protocol = protocols.get(i); if (protocol == Protocol.HTTP_1_0) continue; // No HTTP/1.0 for NPN. result.writeByte(protocol.toString().length()); result.writeUtf8(protocol.toString()); } return result.readByteArray(); }
private static String bodyToString(final Request request) { try { final Request copy = request.newBuilder().build(); final Buffer buffer = new Buffer(); copy.body().writeTo(buffer); return buffer.readUtf8(); } catch (final IOException e) { return "did not work"; } }
@Override public RequestBody toBody(T value) { Buffer buffer = new Buffer(); Writer writer = new OutputStreamWriter(buffer.outputStream(), Util.UTF_8); try { typeAdapter.toJson(writer, value); writer.flush(); } catch (IOException e) { throw new AssertionError(e); // Writing to Buffer does no I/O. } return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); }
@Override public RequestBody convert(T value) throws IOException { Buffer buffer = new Buffer(); Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); try { gson.toJson(value, type, writer); writer.flush(); } catch (IOException e) { throw new AssertionError(e); // Writing to Buffer does no I/O. } return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); }
void headers(boolean outFinished, int streamId, List<Header> headerBlock) throws IOException { if (closed) throw new IOException("closed"); if (hpackBuffer.size() != 0) throw new IllegalStateException(); hpackWriter.writeHeaders(headerBlock); long byteCount = hpackBuffer.size(); int length = (int) Math.min(maxFrameSize, byteCount); byte type = TYPE_HEADERS; byte flags = byteCount == length ? FLAG_END_HEADERS : 0; if (outFinished) flags |= FLAG_END_STREAM; frameHeader(streamId, length, type, flags); sink.write(hpackBuffer, length); if (byteCount > length) writeContinuationFrames(streamId, byteCount - length); }
@Override public Source source(File file) throws FileNotFoundException { Buffer result = files.get(file); if (result == null) throw new FileNotFoundException(); final Source source = result.clone(); openSources.put(source, file); return new ForwardingSource(source) { @Override public void close() throws IOException { openSources.remove(source); super.close(); } }; }
@Override public long read(Buffer sink, long byteCount) throws IOException { if (closed) throw new IOException("closed"); if (messageClosed) throw new IllegalStateException("closed"); if (frameBytesRead == frameLength) { if (isFinalFrame) return -1; // We are exhausted and have no continuations. readUntilNonControlFrame(); if (opcode != OPCODE_CONTINUATION) { throw new ProtocolException("Expected continuation opcode. Got: " + toHexString(opcode)); } if (isFinalFrame && frameLength == 0) { return -1; // Fast-path for empty final frame. } } long toRead = Math.min(byteCount, frameLength - frameBytesRead); long read; if (isMasked) { toRead = Math.min(toRead, maskBuffer.length); read = source.read(maskBuffer, 0, (int) toRead); if (read == -1) throw new EOFException(); toggleMask(maskBuffer, read, maskKey, frameBytesRead); sink.write(maskBuffer, 0, (int) read); } else { read = source.read(sink, toRead); if (read == -1) throw new EOFException(); } frameBytesRead += read; return read; }
private void logRequest(Request request, Response response, String body) throws IOException { if (Config.LOGS_ENABLED) { String requestString = request.method() + " " + request.urlString(); if (!request.method().toLowerCase().equals("get")) { Buffer buffer = new Buffer(); request.body().writeTo(buffer); requestString += " " + buffer.readUtf8(); } if (response.code() >= 400) { LogHelper.d(TAG, "request failed " + requestString); } else { LogHelper.d(TAG, "request success " + requestString); } LogHelper.i(TAG, "response = " + response.code() + " " + body); } }
@Test public void readSendsWindowUpdateHttp2() throws Exception { peer.setVariantAndClient(HTTP_2, false); int windowSize = 100; int windowUpdateThreshold = 50; // Write the mocking script. peer.acceptFrame(); // SYN_STREAM peer.sendFrame().synReply(false, 3, headerEntries("a", "android")); for (int i = 0; i < 3; i++) { // Send frames of summing to size 50, which is windowUpdateThreshold. peer.sendFrame().data(false, 3, data(24), 24); peer.sendFrame().data(false, 3, data(25), 25); peer.sendFrame().data(false, 3, data(1), 1); peer.acceptFrame(); // connection WINDOW UPDATE peer.acceptFrame(); // stream WINDOW UPDATE } peer.sendFrame().data(true, 3, data(0), 0); peer.play(); // Play it back. FramedConnection connection = connection(peer, HTTP_2); connection.okHttpSettings.set(INITIAL_WINDOW_SIZE, 0, windowSize); FramedStream stream = connection.newStream(headerEntries("b", "banana"), false, true); assertEquals(0, stream.unacknowledgedBytesRead); assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); Source in = stream.getSource(); Buffer buffer = new Buffer(); buffer.writeAll(in); assertEquals(-1, in.read(buffer, 1)); assertEquals(150, buffer.size()); MockSpdyPeer.InFrame synStream = peer.takeFrame(); assertEquals(TYPE_HEADERS, synStream.type); for (int i = 0; i < 3; i++) { List<Integer> windowUpdateStreamIds = new ArrayList<>(2); for (int j = 0; j < 2; j++) { MockSpdyPeer.InFrame windowUpdate = peer.takeFrame(); assertEquals(TYPE_WINDOW_UPDATE, windowUpdate.type); windowUpdateStreamIds.add(windowUpdate.streamId); assertEquals(windowUpdateThreshold, windowUpdate.windowSizeIncrement); } assertTrue(windowUpdateStreamIds.contains(0)); // connection assertTrue(windowUpdateStreamIds.contains(3)); // stream } }
private void readControlFrame() throws IOException { Buffer buffer = null; if (frameBytesRead < frameLength) { buffer = new Buffer(); if (isClient) { source.readFully(buffer, frameLength); } else { while (frameBytesRead < frameLength) { int toRead = (int) Math.min(frameLength - frameBytesRead, maskBuffer.length); int read = source.read(maskBuffer, 0, toRead); if (read == -1) throw new EOFException(); toggleMask(maskBuffer, read, maskKey, frameBytesRead); buffer.write(maskBuffer, 0, read); frameBytesRead += read; } } } switch (opcode) { case OPCODE_CONTROL_PING: frameCallback.onPing(buffer); break; case OPCODE_CONTROL_PONG: frameCallback.onPong(buffer); break; case OPCODE_CONTROL_CLOSE: int code = 1000; String reason = ""; if (buffer != null) { long bufferSize = buffer.size(); if (bufferSize == 1) { throw new ProtocolException("Malformed close payload length of 1."); } else if (bufferSize != 0) { code = buffer.readShort(); validateCloseCode(code, false); reason = buffer.readUtf8(); } } frameCallback.onClose(code, reason); closed = true; break; default: throw new ProtocolException("Unknown control opcode: " + toHexString(opcode)); } }
@Override public synchronized void pushPromise( int streamId, int promisedStreamId, List<Header> requestHeaders) throws IOException { if (closed) throw new IOException("closed"); if (hpackBuffer.size() != 0) throw new IllegalStateException(); hpackWriter.writeHeaders(requestHeaders); long byteCount = hpackBuffer.size(); int length = (int) Math.min(maxFrameSize - 4, byteCount); byte type = TYPE_PUSH_PROMISE; byte flags = byteCount == length ? FLAG_END_HEADERS : 0; frameHeader(streamId, length + 4, type, flags); sink.writeInt(promisedStreamId & 0x7fffffff); sink.write(hpackBuffer, length); if (byteCount > length) writeContinuationFrames(streamId, byteCount - length); }
private List<Certificate> readCertificateList(BufferedSource source) throws IOException { int length = readInt(source); if (length == -1) return Collections.emptyList(); // OkHttp v1.2 used -1 to indicate null. try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); List<Certificate> result = new ArrayList<>(length); for (int i = 0; i < length; i++) { String line = source.readUtf8LineStrict(); Buffer bytes = new Buffer(); bytes.write(ByteString.decodeBase64(line)); result.add(certificateFactory.generateCertificate(bytes.inputStream())); } return result; } catch (CertificateException e) { throw new IOException(e.getMessage()); } }
/** * Reads until {@code in} is exhausted or the deadline has been reached. This is careful to not * extend the deadline if one exists already. */ public static boolean skipAll(Source source, int duration, TimeUnit timeUnit) throws IOException { long now = System.nanoTime(); long originalDuration = source.timeout().hasDeadline() ? source.timeout().deadlineNanoTime() - now : Long.MAX_VALUE; source.timeout().deadlineNanoTime(now + Math.min(originalDuration, timeUnit.toNanos(duration))); try { Buffer skipBuffer = new Buffer(); while (source.read(skipBuffer, 2048) != -1) { skipBuffer.clear(); } return true; // Success! The source has been exhausted. } catch (InterruptedIOException e) { return false; // We ran out of time before exhausting the source. } finally { if (originalDuration == Long.MAX_VALUE) { source.timeout().clearDeadline(); } else { source.timeout().deadlineNanoTime(now + originalDuration); } } }
/** * Peeks up to {@code byteCount} bytes from the response body and returns them as a new response * body. If fewer than {@code byteCount} bytes are in the response body, the full response body is * returned. If more than {@code byteCount} bytes are in the response body, the returned value * will be truncated to {@code byteCount} bytes. * * <p>It is an error to call this method after the body has been consumed. * * <p><strong>Warning:</strong> this method loads the requested bytes into memory. Most * applications should set a modest limit on {@code byteCount}, such as 1 MiB. */ public ResponseBody peekBody(long byteCount) throws IOException { BufferedSource source = body.source(); source.request(byteCount); Buffer copy = source.buffer().clone(); // There may be more than byteCount bytes in source.buffer(). If there is, return a prefix. Buffer result; if (copy.size() > byteCount) { result = new Buffer(); result.write(copy, byteCount); copy.clear(); } else { result = copy; } return ResponseBody.create(body.contentType(), result.size(), result); }
@Override public long contentLength() { return buffer.size(); }
@Override public BufferedSource newSource(Path pathRelativeToProjectRoot) throws IOException { Buffer buffer = new Buffer(); buffer.write(fileContents.get(normalizePathToProjectRoot(pathRelativeToProjectRoot))); return buffer; }
@Override public long size(File file) { Buffer buffer = files.get(file); return buffer != null ? buffer.size() : 0L; }
public static Buffer fileToBytes(File file) throws IOException { Buffer result = new Buffer(); result.writeAll(Okio.source(file)); return result; }
private static AbstractCharSequenceAssert<?, String> assertBody(RequestBody body) throws IOException { Buffer buffer = new Buffer(); body.writeTo(buffer); return assertThat(buffer.readByteString().base64()); }
@Override public Response intercept(Chain chain) throws IOException { Level level = this.level; Request request = chain.request(); if (level == Level.NONE) { return chain.proceed(request); } boolean logBody = level == Level.BODY; boolean logHeaders = logBody || level == Level.HEADERS; RequestBody requestBody = request.body(); boolean hasRequestBody = requestBody != null; Connection connection = chain.connection(); Protocol protocol = connection != null ? connection.getProtocol() : Protocol.HTTP_1_1; String requestStartMessage = "--> " + request.method() + ' ' + requestPath(request.httpUrl()) + ' ' + protocol(protocol); if (!logHeaders && hasRequestBody) { requestStartMessage += " (" + requestBody.contentLength() + "-byte body)"; } logger.log(requestStartMessage); if (logHeaders) { Headers headers = request.headers(); for (int i = 0, count = headers.size(); i < count; i++) { logger.log(headers.name(i) + ": " + headers.value(i)); } String endMessage = "--> END " + request.method(); if (logBody && hasRequestBody) { Buffer buffer = new Buffer(); requestBody.writeTo(buffer); Charset charset = UTF8; MediaType contentType = requestBody.contentType(); if (contentType != null) { contentType.charset(UTF8); } logger.log(""); logger.log(buffer.readString(charset)); endMessage += " (" + requestBody.contentLength() + "-byte body)"; } logger.log(endMessage); } long startNs = System.nanoTime(); Response response = chain.proceed(request); long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs); ResponseBody responseBody = response.body(); logger.log( "<-- " + protocol(response.protocol()) + ' ' + response.code() + ' ' + response.message() + " (" + tookMs + "ms" + (!logHeaders ? ", " + responseBody.contentLength() + "-byte body" : "") + ')'); if (logHeaders) { Headers headers = response.headers(); for (int i = 0, count = headers.size(); i < count; i++) { logger.log(headers.name(i) + ": " + headers.value(i)); } String endMessage = "<-- END HTTP"; if (logBody) { BufferedSource source = responseBody.source(); source.request(Long.MAX_VALUE); // Buffer the entire body. Buffer buffer = source.buffer(); Charset charset = UTF8; MediaType contentType = responseBody.contentType(); if (contentType != null) { charset = contentType.charset(UTF8); } if (responseBody.contentLength() != 0) { logger.log(""); logger.log(buffer.clone().readString(charset)); } endMessage += " (" + buffer.size() + "-byte body)"; } logger.log(endMessage); } return response; }
static byte[] toBytes(long v) { okio.Buffer buffer = new okio.Buffer(); buffer.writeLong(v); return buffer.readByteArray(); }