@Override public long contentLength() throws IOException { return content.size(); }
int encodedLength(ByteString bytes) { long len = 0; for (int i = 0; i < bytes.size(); i++) { int b = bytes.getByte(i) & 0xFF; len += CODE_LENGTHS[b]; } return (int) ((len + 7) >> 3); }
/** Returns a new response body that transmits {@code content}. */ public static ResponseBody create(@Nullable MediaType contentType, ByteString content) { Buffer buffer = new Buffer().write(content); return create(contentType, content.size(), buffer); }
private synchronized boolean send(ByteString data, int formatOpcode) { // Don't send new frames after we've failed or enqueued a close frame. if (failed || enqueuedClose) return false; // If this frame overflows the buffer, reject it and close the web socket. if (queueSize + data.size() > MAX_QUEUE_SIZE) { close(CLOSE_CLIENT_GOING_AWAY, null); return false; } // Enqueue the message frame. queueSize += data.size(); messageAndCloseQueue.add(new Message(formatOpcode, data)); runWriter(); return true; }
void commit(long upstreamSize) throws IOException { // Write metadata to the end of the file. writeMetadata(upstreamSize); file.getChannel().force(false); // Once everything else is in place we can swap the dirty header for a clean one. writeHeader(PREFIX_CLEAN, upstreamSize, metadata.size()); file.getChannel().force(false); // This file is complete. synchronized (Relay.this) { complete = true; } closeQuietly(upstream); upstream = null; }
/** * An HTTP/2 response cannot contain uppercase header characters and must be treated as * malformed. */ static ByteString checkLowercase(ByteString name) throws IOException { for (int i = 0, length = name.size(); i < length; i++) { byte c = name.getByte(i); if (c >= 'A' && c <= 'Z') { throw new IOException("PROTOCOL_ERROR response malformed: mixed case name: " + name.utf8()); } } return name; } }
private void writeMetadata(long upstreamSize) throws IOException { Buffer metadataBuffer = new Buffer(); metadataBuffer.write(metadata); FileOperator fileOperator = new FileOperator(file.getChannel()); fileOperator.write(FILE_HEADER_SIZE + upstreamSize, metadataBuffer, metadata.size()); }
void writeByteString(ByteString data) throws IOException { if (useCompression && Huffman.get().encodedLength(data) < data.size()) { Buffer huffmanBuffer = new Buffer(); Huffman.get().encode(data, huffmanBuffer); ByteString huffmanBytes = huffmanBuffer.readByteString(); writeInt(huffmanBytes.size(), PREFIX_7_BITS, 0x80); out.write(huffmanBytes); } else { writeInt(data.size(), PREFIX_7_BITS, 0); out.write(data); } }
/** * Returns true if the first bytes of {@link #source} are {@code key} followed by a colon or * a newline. */ private boolean isKey(ByteString key) throws IOException { if (source.rangeEquals(0, key)) { byte nextByte = source.getBuffer().getByte(key.size()); return nextByte == ':' || nextByte == '\r' || nextByte == '\n'; } return false; }
synchronized boolean close(int code, String reason, long cancelAfterCloseMillis) { validateCloseCode(code); ByteString reasonBytes = null; if (reason != null) { reasonBytes = ByteString.encodeUtf8(reason); if (reasonBytes.size() > CLOSE_MESSAGE_MAX) { throw new IllegalArgumentException("reason.size() > " + CLOSE_MESSAGE_MAX + ": " + reason); } } if (failed || enqueuedClose) return false; // Immediately prevent further frames from being enqueued. enqueuedClose = true; // Enqueue the close frame. messageAndCloseQueue.add(new Close(code, reasonBytes, cancelAfterCloseMillis)); runWriter(); return true; }
@Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) { if (debugData.size() > 0) { // TODO: log the debugData } // Copy the streams first. We don't want to hold a lock when we call receiveRstStream(). Http2Stream[] streamsCopy; synchronized (Http2Connection.this) { streamsCopy = streams.values().toArray(new Http2Stream[streams.size()]); shutdown = true; } // Fail all streams created after the last good stream ID. for (Http2Stream http2Stream : streamsCopy) { if (http2Stream.getId() > lastGoodStreamId && http2Stream.isLocallyInitiated()) { http2Stream.receiveRstStream(REFUSED_STREAM); removeStream(http2Stream.getId()); } } }
void encode(ByteString data, BufferedSink sink) throws IOException { long current = 0; int n = 0; for (int i = 0; i < data.size(); i++) { int b = data.getByte(i) & 0xFF; int code = CODES[b]; int nbits = CODE_LENGTHS[b]; current <<= nbits; current |= code; n += nbits; while (n >= 8) { n -= 8; sink.writeByte(((int) (current >> n))); } } if (n > 0) { current <<= (8 - n); current |= (0xFF >>> n); sink.writeByte((int) current); } }
/** * Creates a relay that reads a recorded stream from {@code file}. * * <p><strong>Warning:</strong> callers to this method must immediately call {@link #newSource} to * create a source and close that when they're done. Otherwise a handle to {@code file} will be * leaked. */ public static Relay read(File file) throws IOException { RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); FileOperator fileOperator = new FileOperator(randomAccessFile.getChannel()); // Read the header. Buffer header = new Buffer(); fileOperator.read(0, header, FILE_HEADER_SIZE); ByteString prefix = header.readByteString(PREFIX_CLEAN.size()); if (!prefix.equals(PREFIX_CLEAN)) throw new IOException("unreadable cache file"); long upstreamSize = header.readLong(); long metadataSize = header.readLong(); // Read the metadata. Buffer metadataBuffer = new Buffer(); fileOperator.read(FILE_HEADER_SIZE + upstreamSize, metadataBuffer, metadataSize); ByteString metadata = metadataBuffer.readByteString(); // Return the result. return new Relay(randomAccessFile, null, upstreamSize, metadata, 0L); }
public void readConnectionPreface(Handler handler) throws IOException { if (client) { // The client reads the initial SETTINGS frame. if (!nextFrame(true, handler)) { throw ioException("Required SETTINGS preface not received"); } } else { // The server reads the CONNECTION_PREFACE byte string. ByteString connectionPreface = source.readByteString(CONNECTION_PREFACE.size()); if (logger.isLoggable(FINE)) logger.fine(format("<< CONNECTION %s", connectionPreface.hex())); if (!CONNECTION_PREFACE.equals(connectionPreface)) { throw ioException("Expected a connection header but was %s", connectionPreface.utf8()); } } }
@Override public T convert(ResponseBody value) throws IOException { BufferedSource source = value.source(); try { // Moshi has no document-level API so the responsibility of BOM skipping falls to whatever // is delegating to it. Since it's a UTF-8-only library as well we only honor the UTF-8 BOM. if (source.rangeEquals(0, UTF8_BOM)) { source.skip(UTF8_BOM.size()); } JsonReader reader = JsonReader.of(source); T result = adapter.fromJson(reader); if (reader.peek() != JsonReader.Token.END_DOCUMENT) { throw new JsonDataException("JSON document was not fully consumed."); } return result; } finally { value.close(); } } }
private void writeControlFrame(int opcode, ByteString payload) throws IOException { if (writerClosed) throw new IOException("closed"); int length = payload.size(); if (length > PAYLOAD_BYTE_MAX) { throw new IllegalArgumentException( "Payload size must be less than or equal to " + PAYLOAD_BYTE_MAX); } int b0 = B0_FLAG_FIN | opcode; sinkBuffer.writeByte(b0); int b1 = length; if (isClient) { b1 |= B1_FLAG_MASK; sinkBuffer.writeByte(b1); random.nextBytes(maskKey); sinkBuffer.write(maskKey); if (length > 0) { long payloadStart = sinkBuffer.size(); sinkBuffer.write(payload); sinkBuffer.readAndWriteUnsafe(maskCursor); maskCursor.seek(payloadStart); toggleMask(maskCursor, maskKey); maskCursor.close(); } } else { sinkBuffer.writeByte(b1); sinkBuffer.write(payload); } sink.flush(); }
@Test public void snapshotReportsAccurateSize() { Buffer buf = new Buffer().write(new byte[] { 0, 1, 2, 3 }); assertEquals(1, buf.snapshot(1).size()); } }
@Test public void multipleSegments() throws Exception { HashingSink hashingSink = HashingSink.sha256(sink); source.write(r32k); hashingSink.write(source, r32k.size()); assertEquals(SHA256_r32k, hashingSink.hash()); }
@Test public void readFromPrefixOfBuffer() throws Exception { source.writeUtf8("z"); source.write(r32k); source.skip(1); source.writeUtf8(TestUtil.repeat('z', SEGMENT_SIZE * 2 - 1)); HashingSink hashingSink = HashingSink.sha256(sink); hashingSink.write(source, r32k.size()); assertEquals(SHA256_r32k, hashingSink.hash()); } }