diff --git a/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java b/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java
index d485955b72..b20f00715d 100644
--- a/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java
+++ b/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java
@@ -37,6 +37,7 @@ public class ByteProcessorTest {
assertEquals(16, buf.forEachByte(14, length - 14, ByteProcessor.FIND_NON_LF));
assertEquals(19, buf.forEachByte(16, length - 16, ByteProcessor.FIND_NUL));
assertEquals(21, buf.forEachByte(19, length - 19, ByteProcessor.FIND_NON_NUL));
+ assertEquals(24, buf.forEachByte(19, length - 19, ByteProcessor.FIND_ASCII_SPACE));
assertEquals(24, buf.forEachByte(21, length - 21, ByteProcessor.FIND_LINEAR_WHITESPACE));
assertEquals(28, buf.forEachByte(24, length - 24, ByteProcessor.FIND_NON_LINEAR_WHITESPACE));
assertEquals(-1, buf.forEachByte(28, length - 28, ByteProcessor.FIND_LINEAR_WHITESPACE));
@@ -51,6 +52,7 @@ public class ByteProcessorTest {
final int length = buf.readableBytes();
assertEquals(27, buf.forEachByteDesc(0, length, ByteProcessor.FIND_LINEAR_WHITESPACE));
+ assertEquals(25, buf.forEachByteDesc(0, length, ByteProcessor.FIND_ASCII_SPACE));
assertEquals(23, buf.forEachByteDesc(0, 28, ByteProcessor.FIND_NON_LINEAR_WHITESPACE));
assertEquals(20, buf.forEachByteDesc(0, 24, ByteProcessor.FIND_NUL));
assertEquals(18, buf.forEachByteDesc(0, 21, ByteProcessor.FIND_NON_NUL));
diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseDecoder.java
index c6351c47bf..21491971ef 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseDecoder.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseDecoder.java
@@ -116,7 +116,7 @@ public class HttpResponseDecoder extends HttpObjectDecoder {
protected HttpMessage createMessage(String[] initialLine) {
return new DefaultHttpResponse(
HttpVersion.valueOf(initialLine[0]),
- new HttpResponseStatus(Integer.parseInt(initialLine[1]), initialLine[2]), validateHeaders);
+ HttpResponseStatus.valueOf(Integer.parseInt(initialLine[1]), initialLine[2]), validateHeaders);
}
@Override
diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java
index 026866ebcc..b7e2c10d45 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java
@@ -15,13 +15,15 @@
*/
package io.netty.handler.codec.http;
-import static io.netty.handler.codec.http.HttpConstants.SP;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.AsciiString;
-import io.netty.util.ByteProcessor;
import io.netty.util.CharsetUtil;
+import static io.netty.handler.codec.http.HttpConstants.SP;
+import static io.netty.util.ByteProcessor.FIND_ASCII_SPACE;
+import static java.lang.Integer.parseInt;
+
/**
* The response code and its description of HTTP or its derived protocols, such as
* RTSP and
@@ -324,10 +326,15 @@ public class HttpResponseStatus implements Comparable {
/**
* Returns the {@link HttpResponseStatus} represented by the specified code.
- * If the specified code is a standard HTTP getStatus code, a cached instance
+ * If the specified code is a standard HTTP status code, a cached instance
* will be returned. Otherwise, a new instance will be returned.
*/
public static HttpResponseStatus valueOf(int code) {
+ HttpResponseStatus status = valueOf0(code);
+ return status != null ? status : new HttpResponseStatus(code);
+ }
+
+ private static HttpResponseStatus valueOf0(int code) {
switch (code) {
case 100:
return CONTINUE;
@@ -442,12 +449,25 @@ public class HttpResponseStatus implements Comparable {
case 511:
return NETWORK_AUTHENTICATION_REQUIRED;
}
-
- return new HttpResponseStatus(code);
+ return null;
}
/**
- * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
+ * Returns the {@link HttpResponseStatus} represented by the specified {@code code} and {@code reasonPhrase}.
+ * If the specified code is a standard HTTP status {@code code} and {@code reasonPhrase}, a cached instance
+ * will be returned. Otherwise, a new instance will be returned.
+ * @param code The response code value.
+ * @param reasonPhrase The response code reason phrase.
+ * @return the {@link HttpResponseStatus} represented by the specified {@code code} and {@code reasonPhrase}.
+ */
+ public static HttpResponseStatus valueOf(int code, String reasonPhrase) {
+ HttpResponseStatus responseStatus = valueOf0(code);
+ return responseStatus != null && responseStatus.reasonPhrase().contentEquals(reasonPhrase) ? responseStatus :
+ new HttpResponseStatus(code, reasonPhrase);
+ }
+
+ /**
+ * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
*
* - {@code statusCode} (e.g. 200)
* - {@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)
@@ -456,79 +476,25 @@ public class HttpResponseStatus implements Comparable {
* @throws IllegalArgumentException if the specified status line is malformed
*/
public static HttpResponseStatus parseLine(CharSequence line) {
- String status = line.toString();
- try {
- int space = status.indexOf(' ');
- if (space == -1) {
- return valueOf(Integer.parseInt(status));
- } else {
- int code = Integer.parseInt(status.substring(0, space));
- String reasonPhrase = status.substring(space + 1);
- HttpResponseStatus responseStatus = valueOf(code);
- if (responseStatus.reasonPhrase().contentEquals(reasonPhrase)) {
- return responseStatus;
- } else {
- return new HttpResponseStatus(code, reasonPhrase);
- }
- }
- } catch (Exception e) {
- throw new IllegalArgumentException("malformed status line: " + status, e);
- }
+ return (line instanceof AsciiString) ? parseLine((AsciiString) line) : parseLine(line.toString());
}
- private static final class HttpStatusLineProcessor implements ByteProcessor {
- private static final byte ASCII_SPACE = (byte) ' ';
- private final AsciiString string;
- private int i;
- /**
- * 0 = New or havn't seen {@link #ASCII_SPACE}.
- * 1 = Last byte was {@link #ASCII_SPACE}.
- * 2 = Terminal State. Processed the byte after {@link #ASCII_SPACE}, and parsed the status line.
- * 3 = Terminal State. There was no byte after {@link #ASCII_SPACE} but status has been parsed with what we saw.
- */
- private int state;
- private HttpResponseStatus status;
-
- public HttpStatusLineProcessor(AsciiString string) {
- this.string = string;
- }
-
- @Override
- public boolean process(byte value) {
- switch (state) {
- case 0:
- if (value == ASCII_SPACE) {
- state = 1;
- }
- break;
- case 1:
- parseStatus(i);
- state = 2;
- return false;
- default:
- break;
- }
- ++i;
- return true;
- }
-
- private void parseStatus(int codeEnd) {
- int code = string.parseInt(0, codeEnd);
- status = valueOf(code);
- if (codeEnd < string.length()) {
- String actualReason = string.toString(codeEnd + 1, string.length());
- if (!status.reasonPhrase().contentEquals(actualReason)) {
- status = new HttpResponseStatus(code, actualReason);
- }
- }
- }
-
- public HttpResponseStatus status() {
- if (state <= 1) {
- parseStatus(string.length());
- state = 3;
- }
- return status;
+ /**
+ * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
+ *
+ * - {@code statusCode} (e.g. 200)
+ * - {@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)
+ *
+ *
+ * @throws IllegalArgumentException if the specified status line is malformed
+ */
+ public static HttpResponseStatus parseLine(String line) {
+ try {
+ int space = line.indexOf(' ');
+ return space == -1 ? valueOf(parseInt(line)) :
+ valueOf(parseInt(line.substring(0, space)), line.substring(space + 1));
+ } catch (Exception e) {
+ throw new IllegalArgumentException("malformed status line: " + line, e);
}
}
@@ -543,13 +509,8 @@ public class HttpResponseStatus implements Comparable {
*/
public static HttpResponseStatus parseLine(AsciiString line) {
try {
- HttpStatusLineProcessor processor = new HttpStatusLineProcessor(line);
- line.forEachByte(processor);
- HttpResponseStatus status = processor.status();
- if (status == null) {
- throw new IllegalArgumentException("unable to get status after parsing input");
- }
- return status;
+ int space = line.forEachByte(FIND_ASCII_SPACE);
+ return space == -1 ? valueOf(line.parseInt()) : valueOf(line.parseInt(0, space), line.toString(space + 1));
} catch (Exception e) {
throw new IllegalArgumentException("malformed status line: " + line, e);
}
@@ -598,10 +559,11 @@ public class HttpResponseStatus implements Comparable {
}
this.code = code;
- codeAsText = new AsciiString(Integer.toString(code));
+ String codeString = Integer.toString(code);
+ codeAsText = new AsciiString(codeString);
this.reasonPhrase = reasonPhrase;
if (bytes) {
- this.bytes = (code + " " + reasonPhrase).getBytes(CharsetUtil.US_ASCII);
+ this.bytes = (codeString + ' ' + reasonPhrase).getBytes(CharsetUtil.US_ASCII);
} else {
this.bytes = null;
}
diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseStatusTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseStatusTest.java
new file mode 100644
index 0000000000..2bc83bac8f
--- /dev/null
+++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseStatusTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2018 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 io.netty.handler.codec.http;
+
+import io.netty.util.AsciiString;
+import org.junit.Test;
+
+import static io.netty.handler.codec.http.HttpResponseStatus.parseLine;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+public class HttpResponseStatusTest {
+ @Test
+ public void parseLineStringJustCode() {
+ assertSame(HttpResponseStatus.OK, parseLine("200"));
+ }
+
+ @Test
+ public void parseLineStringCodeAndPhrase() {
+ assertSame(HttpResponseStatus.OK, parseLine("200 OK"));
+ }
+
+ @Test
+ public void parseLineStringCustomCode() {
+ HttpResponseStatus customStatus = parseLine("612");
+ assertEquals(612, customStatus.code());
+ }
+
+ @Test
+ public void parseLineStringCustomCodeAndPhrase() {
+ HttpResponseStatus customStatus = parseLine("612 FOO");
+ assertEquals(612, customStatus.code());
+ assertEquals("FOO", customStatus.reasonPhrase());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseLineStringMalformedCode() {
+ parseLine("200a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseLineStringMalformedCodeWithPhrase() {
+ parseLine("200a foo");
+ }
+
+ @Test
+ public void parseLineAsciiStringJustCode() {
+ assertSame(HttpResponseStatus.OK, parseLine(new AsciiString("200")));
+ }
+
+ @Test
+ public void parseLineAsciiStringCodeAndPhrase() {
+ assertSame(HttpResponseStatus.OK, parseLine(new AsciiString("200 OK")));
+ }
+
+ @Test
+ public void parseLineAsciiStringCustomCode() {
+ HttpResponseStatus customStatus = parseLine(new AsciiString("612"));
+ assertEquals(612, customStatus.code());
+ }
+
+ @Test
+ public void parseLineAsciiStringCustomCodeAndPhrase() {
+ HttpResponseStatus customStatus = parseLine(new AsciiString("612 FOO"));
+ assertEquals(612, customStatus.code());
+ assertEquals("FOO", customStatus.reasonPhrase());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseLineAsciiStringMalformedCode() {
+ parseLine(new AsciiString("200a"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseLineAsciiStringMalformedCodeWithPhrase() {
+ parseLine(new AsciiString("200a foo"));
+ }
+}
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java
index 66a9e52845..5f808db8df 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java
@@ -44,6 +44,7 @@ import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderNames.COOKIE;
import static io.netty.handler.codec.http.HttpHeaderNames.TE;
import static io.netty.handler.codec.http.HttpHeaderValues.TRAILERS;
+import static io.netty.handler.codec.http.HttpResponseStatus.parseLine;
import static io.netty.handler.codec.http.HttpScheme.HTTP;
import static io.netty.handler.codec.http.HttpScheme.HTTPS;
import static io.netty.handler.codec.http.HttpUtil.isAsteriskForm;
@@ -183,7 +184,7 @@ public final class HttpConversionUtil {
public static HttpResponseStatus parseStatus(CharSequence status) throws Http2Exception {
HttpResponseStatus result;
try {
- result = HttpResponseStatus.parseLine(status);
+ result = parseLine(status);
if (result == HttpResponseStatus.SWITCHING_PROTOCOLS) {
throw connectionError(PROTOCOL_ERROR, "Invalid HTTP/2 status code '%d'", result.code());
}
diff --git a/common/src/main/java/io/netty/util/ByteProcessor.java b/common/src/main/java/io/netty/util/ByteProcessor.java
index 1ca1e58d7e..31cd6ff824 100644
--- a/common/src/main/java/io/netty/util/ByteProcessor.java
+++ b/common/src/main/java/io/netty/util/ByteProcessor.java
@@ -14,6 +14,11 @@
*/
package io.netty.util;
+import static io.netty.util.ByteProcessorUtils.CARRIAGE_RETURN;
+import static io.netty.util.ByteProcessorUtils.HTAB;
+import static io.netty.util.ByteProcessorUtils.LINE_FEED;
+import static io.netty.util.ByteProcessorUtils.SPACE;
+
/**
* Provides a mechanism to iterate over a collection of bytes.
*/
@@ -63,22 +68,22 @@ public interface ByteProcessor {
/**
* Aborts on a {@code CR ('\r')}.
*/
- ByteProcessor FIND_CR = new IndexOfProcessor((byte) '\r');
+ ByteProcessor FIND_CR = new IndexOfProcessor(CARRIAGE_RETURN);
/**
* Aborts on a non-{@code CR ('\r')}.
*/
- ByteProcessor FIND_NON_CR = new IndexNotOfProcessor((byte) '\r');
+ ByteProcessor FIND_NON_CR = new IndexNotOfProcessor(CARRIAGE_RETURN);
/**
* Aborts on a {@code LF ('\n')}.
*/
- ByteProcessor FIND_LF = new IndexOfProcessor((byte) '\n');
+ ByteProcessor FIND_LF = new IndexOfProcessor(LINE_FEED);
/**
* Aborts on a non-{@code LF ('\n')}.
*/
- ByteProcessor FIND_NON_LF = new IndexNotOfProcessor((byte) '\n');
+ ByteProcessor FIND_NON_LF = new IndexNotOfProcessor(LINE_FEED);
/**
* Aborts on a semicolon {@code (';')}.
@@ -90,13 +95,18 @@ public interface ByteProcessor {
*/
ByteProcessor FIND_COMMA = new IndexOfProcessor((byte) ',');
+ /**
+ * Aborts on a ascii space character ({@code ' '}).
+ */
+ ByteProcessor FIND_ASCII_SPACE = new IndexOfProcessor(SPACE);
+
/**
* Aborts on a {@code CR ('\r')} or a {@code LF ('\n')}.
*/
ByteProcessor FIND_CRLF = new ByteProcessor() {
@Override
public boolean process(byte value) {
- return value != '\r' && value != '\n';
+ return value != CARRIAGE_RETURN && value != LINE_FEED;
}
};
@@ -106,7 +116,7 @@ public interface ByteProcessor {
ByteProcessor FIND_NON_CRLF = new ByteProcessor() {
@Override
public boolean process(byte value) {
- return value == '\r' || value == '\n';
+ return value == CARRIAGE_RETURN || value == LINE_FEED;
}
};
@@ -116,7 +126,7 @@ public interface ByteProcessor {
ByteProcessor FIND_LINEAR_WHITESPACE = new ByteProcessor() {
@Override
public boolean process(byte value) {
- return value != ' ' && value != '\t';
+ return value != SPACE && value != HTAB;
}
};
@@ -126,7 +136,7 @@ public interface ByteProcessor {
ByteProcessor FIND_NON_LINEAR_WHITESPACE = new ByteProcessor() {
@Override
public boolean process(byte value) {
- return value == ' ' || value == '\t';
+ return value == SPACE || value == HTAB;
}
};
diff --git a/common/src/main/java/io/netty/util/ByteProcessorUtils.java b/common/src/main/java/io/netty/util/ByteProcessorUtils.java
new file mode 100644
index 0000000000..c642a0ab12
--- /dev/null
+++ b/common/src/main/java/io/netty/util/ByteProcessorUtils.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2018 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 io.netty.util;
+
+final class ByteProcessorUtils {
+ static final byte SPACE = (byte) ' ';
+ static final byte HTAB = (byte) '\t';
+ static final byte CARRIAGE_RETURN = (byte) '\r';
+ static final byte LINE_FEED = (byte) '\n';
+
+ private ByteProcessorUtils() {
+ }
+}