mirror of
https://github.com/square/okhttp.git
synced 2026-01-21 03:41:07 +03:00
Promote line reading to BufferedSource.
This commit is contained in:
@@ -145,7 +145,7 @@ public final class HttpConnection {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
String statusLineString = readLine();
|
||||
String statusLineString = source.readUtf8Line(true);
|
||||
StatusLine statusLine = new StatusLine(statusLineString);
|
||||
|
||||
Response.Builder responseBuilder = new Response.Builder()
|
||||
@@ -166,28 +166,11 @@ public final class HttpConnection {
|
||||
/** Reads headers or trailers into {@code builder}. */
|
||||
public void readHeaders(Headers.Builder builder) throws IOException {
|
||||
// parse the result headers until the first blank line
|
||||
for (String line; (line = readLine()).length() != 0; ) {
|
||||
for (String line; (line = source.readUtf8Line(true)).length() != 0; ) {
|
||||
builder.addLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
private String readLine() throws IOException {
|
||||
long newline = source.seek((byte) '\n');
|
||||
|
||||
if (newline > 0 && source.buffer().getByte(newline - 1) == '\r') {
|
||||
// Read everything until '\r\n', then skip the '\r\n'.
|
||||
String result = source.readUtf8((int) (newline - 1));
|
||||
source.skip(2);
|
||||
return result;
|
||||
|
||||
} else {
|
||||
// Read everything until '\n', then skip the '\n'.
|
||||
String result = source.readUtf8((int) (newline));
|
||||
source.skip(1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the response body so that the connection can be reused and the
|
||||
* cache entry can be completed. This needs to be done judiciously, since it
|
||||
@@ -530,9 +513,9 @@ public final class HttpConnection {
|
||||
private void readChunkSize() throws IOException {
|
||||
// read the suffix of the previous chunk
|
||||
if (bytesRemainingInChunk != NO_CHUNK_YET) {
|
||||
readLine();
|
||||
source.readUtf8Line(true);
|
||||
}
|
||||
String chunkSizeString = readLine();
|
||||
String chunkSizeString = source.readUtf8Line(true);
|
||||
int index = chunkSizeString.indexOf(";");
|
||||
if (index != -1) {
|
||||
chunkSizeString = chunkSizeString.substring(0, index);
|
||||
|
||||
@@ -40,14 +40,19 @@ public interface BufferedSource extends Source {
|
||||
*/
|
||||
void require(long byteCount) throws IOException;
|
||||
|
||||
/** Removes a byte from the front of this buffer and returns it. */
|
||||
byte readByte() throws IOException;
|
||||
|
||||
/** Removes a Big-Endian short from the front of this buffer and returns it. */
|
||||
short readShort() throws IOException;
|
||||
|
||||
/** Removes a Little-Endian short from the front of this buffer and returns it. */
|
||||
int readShortLe() throws IOException;
|
||||
|
||||
/** Removes a Big-Endian int from the front of this buffer and returns it. */
|
||||
int readInt() throws IOException;
|
||||
|
||||
/** Removes a Little-Endian int from the front of this buffer and returns it. */
|
||||
int readIntLe() throws IOException;
|
||||
|
||||
/**
|
||||
@@ -57,10 +62,34 @@ public interface BufferedSource extends Source {
|
||||
*/
|
||||
void skip(long byteCount) throws IOException;
|
||||
|
||||
/** Removes {@code byteCount} bytes from this and returns them as a byte string. */
|
||||
ByteString readByteString(int byteCount) throws IOException;
|
||||
|
||||
/**
|
||||
* Removes {@code byteCount} bytes from this, decodes them as UTF-8 and
|
||||
* returns the string.
|
||||
*/
|
||||
String readUtf8(int byteCount) throws IOException;
|
||||
|
||||
/**
|
||||
* Removes and returns characters up to but not including the next line break.
|
||||
* A line break is either {@code "\n"} or {@code "\r\n"}; these characters are
|
||||
* not included in the result.
|
||||
*
|
||||
* <p>This method supports two ways to handle the end of the stream:
|
||||
* <ul>
|
||||
* <li><strong>Throw on EOF.</strong> Every call must consume either '\r\n'
|
||||
* or '\n'. If these characters are absent in the stream, an {@link
|
||||
* java.io.EOFException} is thrown. Use this for machine-generated data
|
||||
* where a missing line break implies truncated input.
|
||||
* <li><strong>Don't throw, just like BufferedReader.</strong> If the source
|
||||
* doesn't end with a line break then an implicit line break is assumed.
|
||||
* Null is returned once the source is exhausted. Use this for
|
||||
* human-generated data, where a trailing line breaks are optional.
|
||||
* </ul>
|
||||
*/
|
||||
String readUtf8Line(boolean throwOnEof) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the index of {@code b} in the buffer, refilling it if necessary
|
||||
* until it is found. This reads an unbounded number of bytes into the buffer.
|
||||
|
||||
@@ -139,7 +139,6 @@ public final class OkBuffer implements BufferedSource, BufferedSink, Cloneable {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Removes a byte from the front of this buffer and returns it. */
|
||||
@Override public byte readByte() {
|
||||
if (byteCount == 0) throw new IllegalStateException("byteCount == 0");
|
||||
|
||||
@@ -171,7 +170,6 @@ public final class OkBuffer implements BufferedSource, BufferedSink, Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes a Big-Endian short from the front of this buffer and returns it. */
|
||||
@Override public short readShort() {
|
||||
if (byteCount < 2) throw new IllegalArgumentException("byteCount < 2: " + byteCount);
|
||||
|
||||
@@ -201,7 +199,6 @@ public final class OkBuffer implements BufferedSource, BufferedSink, Cloneable {
|
||||
return (short) s;
|
||||
}
|
||||
|
||||
/** Removes a Big-Endian int from the front of this buffer and returns it. */
|
||||
@Override public int readInt() {
|
||||
if (byteCount < 4) throw new IllegalArgumentException("byteCount < 4: " + byteCount);
|
||||
|
||||
@@ -234,22 +231,18 @@ public final class OkBuffer implements BufferedSource, BufferedSink, Cloneable {
|
||||
return i;
|
||||
}
|
||||
|
||||
/** Removes a Little-Endian short from the front of this buffer and returns it. */
|
||||
public int readShortLe() {
|
||||
return Util.reverseBytesShort(readShort());
|
||||
}
|
||||
|
||||
/** Removes a Little-Endian int from the front of this buffer and returns it. */
|
||||
public int readIntLe() {
|
||||
return Util.reverseBytesInt(readInt());
|
||||
}
|
||||
|
||||
/** Removes {@code byteCount} bytes from this and returns them as a byte string. */
|
||||
public ByteString readByteString(int byteCount) {
|
||||
return new ByteString(readBytes(byteCount));
|
||||
}
|
||||
|
||||
/** Removes {@code byteCount} bytes from this, decodes them as UTF-8 and returns the string. */
|
||||
public String readUtf8(int byteCount) {
|
||||
checkOffsetAndCount(this.byteCount, 0, byteCount);
|
||||
if (byteCount == 0) return "";
|
||||
@@ -272,6 +265,28 @@ public final class OkBuffer implements BufferedSource, BufferedSink, Cloneable {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override public String readUtf8Line(boolean throwOnEof) throws EOFException {
|
||||
long newline = indexOf((byte) '\n');
|
||||
|
||||
if (newline == -1) {
|
||||
if (throwOnEof) throw new EOFException();
|
||||
return byteCount != 0 ? readUtf8((int) byteCount) : null;
|
||||
}
|
||||
|
||||
if (newline > 0 && getByte(newline - 1) == '\r') {
|
||||
// Read everything until '\r\n', then skip the '\r\n'.
|
||||
String result = readUtf8((int) (newline - 1));
|
||||
skip(2);
|
||||
return result;
|
||||
|
||||
} else {
|
||||
// Read everything until '\n', then skip the '\n'.
|
||||
String result = readUtf8((int) (newline));
|
||||
skip(1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] readBytes(int byteCount) {
|
||||
checkOffsetAndCount(this.byteCount, 0, byteCount);
|
||||
|
||||
|
||||
@@ -79,6 +79,31 @@ final class RealBufferedSource implements BufferedSource {
|
||||
return buffer.readUtf8(byteCount);
|
||||
}
|
||||
|
||||
@Override public String readUtf8Line(boolean throwOnEof) throws IOException {
|
||||
long start = 0;
|
||||
long newline;
|
||||
while ((newline = buffer.indexOf((byte) '\n', start)) == -1) {
|
||||
start = buffer.byteCount;
|
||||
if (source.read(buffer, Segment.SIZE) == -1) {
|
||||
if (throwOnEof) throw new EOFException();
|
||||
return buffer.byteCount != 0 ? readUtf8((int) buffer.byteCount) : null;
|
||||
}
|
||||
}
|
||||
|
||||
if (newline > 0 && buffer.getByte(newline - 1) == '\r') {
|
||||
// Read everything until '\r\n', then skip the '\r\n'.
|
||||
String result = readUtf8((int) (newline - 1));
|
||||
skip(2);
|
||||
return result;
|
||||
|
||||
} else {
|
||||
// Read everything until '\n', then skip the '\n'.
|
||||
String result = readUtf8((int) (newline));
|
||||
skip(1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public short readShort() throws IOException {
|
||||
require(2);
|
||||
return buffer.readShort();
|
||||
|
||||
22
okio/src/test/java/okio/OkBufferReadUtf8LineTest.java
Normal file
22
okio/src/test/java/okio/OkBufferReadUtf8LineTest.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 okio;
|
||||
|
||||
public final class OkBufferReadUtf8LineTest extends ReadUtf8LineTest {
|
||||
@Override protected BufferedSource newSource(String s) {
|
||||
return new OkBuffer().writeUtf8(s);
|
||||
}
|
||||
}
|
||||
68
okio/src/test/java/okio/ReadUtf8LineTest.java
Normal file
68
okio/src/test/java/okio/ReadUtf8LineTest.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 okio;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public abstract class ReadUtf8LineTest {
|
||||
protected abstract BufferedSource newSource(String s);
|
||||
|
||||
@Test public void readLines() throws IOException {
|
||||
BufferedSource source = newSource("abc\ndef\n");
|
||||
assertEquals("abc", source.readUtf8Line(true));
|
||||
assertEquals("def", source.readUtf8Line(true));
|
||||
try {
|
||||
source.readUtf8Line(true);
|
||||
fail();
|
||||
} catch (EOFException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void emptyLines() throws IOException {
|
||||
BufferedSource source = newSource("\n\n\n");
|
||||
assertEquals("", source.readUtf8Line(true));
|
||||
assertEquals("", source.readUtf8Line(true));
|
||||
assertEquals("", source.readUtf8Line(true));
|
||||
assertTrue(source.exhausted());
|
||||
}
|
||||
|
||||
@Test public void crDroppedPrecedingLf() throws IOException {
|
||||
BufferedSource source = newSource("abc\r\ndef\r\nghi\rjkl\r\n");
|
||||
assertEquals("abc", source.readUtf8Line(true));
|
||||
assertEquals("def", source.readUtf8Line(true));
|
||||
assertEquals("ghi\rjkl", source.readUtf8Line(true));
|
||||
}
|
||||
|
||||
@Test public void bufferedReaderCompatible() throws IOException {
|
||||
BufferedSource source = newSource("abc\ndef");
|
||||
assertEquals("abc", source.readUtf8Line(false));
|
||||
assertEquals("def", source.readUtf8Line(false));
|
||||
assertEquals(null, source.readUtf8Line(false));
|
||||
}
|
||||
|
||||
@Test public void bufferedReaderCompatibleWithTrailingNewline() throws IOException {
|
||||
BufferedSource source = newSource("abc\ndef\n");
|
||||
assertEquals("abc", source.readUtf8Line(false));
|
||||
assertEquals("def", source.readUtf8Line(false));
|
||||
assertEquals(null, source.readUtf8Line(false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 okio;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class RealBufferedSourceReadUtf8LineTest extends ReadUtf8LineTest {
|
||||
/** Returns a buffered source that gets bytes of {@code data} one at a time. */
|
||||
@Override protected BufferedSource newSource(String s) {
|
||||
final OkBuffer buffer = new OkBuffer().writeUtf8(s);
|
||||
|
||||
Source slowSource = new Source() {
|
||||
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
|
||||
return buffer.read(sink, Math.min(1, byteCount));
|
||||
}
|
||||
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
return Okio.buffer(slowSource);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user