Skip to content
Permalink
Browse files

Request.getMethodName, Request.consumeHeaders

  • Loading branch information
apangin committed Dec 9, 2019
1 parent 2011633 commit 2b100577c8970725d0428e712272e003b56f59b0
@@ -23,12 +23,16 @@
import one.nio.pool.PoolException;
import one.nio.pool.SocketPool;
import one.nio.util.Utf8;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;

public class HttpClient extends SocketPool {
protected static final Log log = LogFactory.getLog(HttpClient.class);

private static final int BUFFER_SIZE = 8000;

protected String[] permanentHeaders;
@@ -220,6 +224,9 @@ Response readResponse(int method) throws IOException, HttpException {
} else {
String contentLength = response.getHeader("Content-Length: ");
if (contentLength == null) {
if (log.isDebugEnabled()) {
log.debug("Content-Length unspecified: " + response.toString());
}
throw new HttpException("Content-Length unspecified");
}

@@ -26,6 +26,7 @@
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.Consumer;

public class Request {
public static final int METHOD_GET = 1;
@@ -39,29 +40,30 @@
public static final int METHOD_PATCH = 9;
public static final int NUMBER_OF_METHODS = 10;

public static final byte[] VERB_GET = Utf8.toBytes("GET ");
public static final byte[] VERB_POST = Utf8.toBytes("POST ");
public static final byte[] VERB_HEAD = Utf8.toBytes("HEAD ");
public static final byte[] VERB_OPTIONS = Utf8.toBytes("OPTIONS ");
public static final byte[] VERB_PUT = Utf8.toBytes("PUT ");
public static final byte[] VERB_DELETE = Utf8.toBytes("DELETE ");
public static final byte[] VERB_TRACE = Utf8.toBytes("TRACE ");
public static final byte[] VERB_CONNECT = Utf8.toBytes("CONNECT ");
public static final byte[] VERB_PATCH = Utf8.toBytes("PATCH ");

static final byte[][] VERBS = {
new byte[0],
VERB_GET,
VERB_POST,
VERB_HEAD,
VERB_OPTIONS,
VERB_PUT,
VERB_DELETE,
VERB_TRACE,
VERB_CONNECT,
VERB_PATCH
static final String[] METHODS = {
"",
"GET",
"POST",
"HEAD",
"OPTIONS",
"PUT",
"DELETE",
"TRACE",
"CONNECT",
"PATCH"
};

// Methods followed by a single space character
static final byte[][] VERBS;

static {
VERBS = new byte[METHODS.length][];
VERBS[0] = new byte[0];
for (int i = 1; i < VERBS.length; i++) {
VERBS[i] = Utf8.toBytes(METHODS[i] + ' ');
}
}

private static final byte[] HTTP10_HEADER = Utf8.toBytes(" HTTP/1.0\r\n");
private static final byte[] HTTP11_HEADER = Utf8.toBytes(" HTTP/1.1\r\n");
private static final int PROTOCOL_HEADER_LENGTH = 13;
@@ -75,6 +77,7 @@
private byte[] body;

public Request(int method, String uri, boolean http11) {
assert 0 <= method && method < METHODS.length;
this.method = method;
this.uri = uri;
this.http11 = http11;
@@ -97,6 +100,10 @@ public int getMethod() {
return method;
}

public String getMethodName() {
return METHODS[method];
}

public String getURI() {
return uri;
}
@@ -128,35 +135,35 @@ public String getParameter(String key) {
}

public Iterator<String> getParameters(final String key) {
return new Iterator<String>() {
int cur = findNext(params + 1);

@Override
public boolean hasNext() {
return cur > 0;
}

@Override
public String next() {
int next = uri.indexOf('&', cur);
cur += key.length();
String rawValue = next > 0 ? uri.substring(cur, next) : uri.substring(cur);
cur = findNext(next + 1);
return URLEncoder.decode(rawValue);
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}

private int findNext(int cur) {
while (cur > 0 && !uri.startsWith(key, cur)) {
cur = uri.indexOf('&', cur) + 1;
}
return cur;
}
};
return new Iterator<String>() {
int cur = findNext(params + 1);

@Override
public boolean hasNext() {
return cur > 0;
}

@Override
public String next() {
int next = uri.indexOf('&', cur);
cur += key.length();
String rawValue = next > 0 ? uri.substring(cur, next) : uri.substring(cur);
cur = findNext(next + 1);
return URLEncoder.decode(rawValue);
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}

private int findNext(int cur) {
while (cur > 0 && !uri.startsWith(key, cur)) {
cur = uri.indexOf('&', cur) + 1;
}
return cur;
}
};
}

public String getParameter(String key, String defaultValue) {
@@ -174,7 +181,7 @@ public String getRequiredParameter(String key) {

/**
* @return {@link Iterable} over {@code String} {@code key[=[value]]} parameters
* skipping empty parameters
* skipping empty parameters
*/
public Iterable<Map.Entry<String, String>> getParameters() {
if (params < 0) {
@@ -207,6 +214,15 @@ public String getHeader(String key) {
return null;
}

public void consumeHeaders(String prefix, Consumer<String> suffixConsumer) {
int keyLength = prefix.length();
for (int i = 0; i < headerCount; i++) {
if (headers[i].regionMatches(true, 0, prefix, 0, keyLength)) {
suffixConsumer.accept(headers[i].substring(keyLength));
}
}
}

public String getHeader(String key, String defaultValue) {
String value = getHeader(key);
return value != null ? value : defaultValue;
@@ -0,0 +1,60 @@
/*
* Copyright 2019 Odnoklassniki Ltd, Mail.Ru Group
*
* 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 one.nio.http;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.junit.Assert.assertEquals;

/**
* Unit tests for HTTP header processing facilities.
*
* @author Vadim Tsesko <incubos@yandex.com>
*/
public class HttpHeaderTest {
private static final String HEADER = "X-OK-Custom-Header: ";

private void testHeaderConsumer(final String... values) {
final Request request = new Request(Request.METHOD_GET, "/", true);
for (final String value : values) {
request.addHeader(HEADER + value);
}

final List<String> sink = new ArrayList<>(values.length);
request.consumeHeaders(HEADER, sink::add);
assertEquals(Arrays.asList(values), sink);
}

@Test
public void consumeEmpty() {
testHeaderConsumer();
}

@Test
public void consumeSingle() {
testHeaderConsumer("Value");
}

@Test
public void consumeDouble() {
testHeaderConsumer("First", "Second");
}
}
@@ -35,7 +35,8 @@
* @author Vadim Tsesko <incubos@yandex.com>
*/
public class HttpMethodTest {
private static final String ENDPOINT = "/echoMethod";
private static final String ECHO_METHOD_ENDPOINT = "/echoMethod";
private static final String ECHO_PUT_ENDPOINT = "/echoMethodOnPut";

private static HttpServer server;
private static HttpClient client;
@@ -57,62 +58,69 @@ public static void afterAll() {
@Test
public void get() throws Exception {
assertEquals(
Integer.toString(Request.METHOD_GET),
client.get(ENDPOINT).getBodyUtf8());
Request.METHODS[Request.METHOD_GET],
client.get(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
public void head() throws Exception {
assertNull(client.head(ENDPOINT).getBodyUtf8());
assertNull(client.head(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
public void post() throws Exception {
assertEquals(
Integer.toString(Request.METHOD_POST),
client.post(ENDPOINT).getBodyUtf8());
Request.METHODS[Request.METHOD_POST],
client.post(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
public void put() throws Exception {
assertEquals(
Request.METHODS[Request.METHOD_PUT],
client.put(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
public void putMatcher() throws Exception {
assertEquals(
"echoMethodOnPut",
client.put(ENDPOINT).getBodyUtf8());
client.put(ECHO_PUT_ENDPOINT).getBodyUtf8());
}

@Test
public void options() throws Exception {
assertEquals(
Integer.toString(Request.METHOD_OPTIONS),
client.options(ENDPOINT).getBodyUtf8());
Request.METHODS[Request.METHOD_OPTIONS],
client.options(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
public void delete() throws Exception {
assertEquals(
Integer.toString(Request.METHOD_DELETE),
client.delete(ENDPOINT).getBodyUtf8());
Request.METHODS[Request.METHOD_DELETE],
client.delete(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
public void trace() throws Exception {
assertEquals(
Integer.toString(Request.METHOD_TRACE),
client.trace(ENDPOINT).getBodyUtf8());
Request.METHODS[Request.METHOD_TRACE],
client.trace(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
public void connect() throws Exception {
assertEquals(
Integer.toString(Request.METHOD_CONNECT),
client.connect(ENDPOINT).getBodyUtf8());
Request.METHODS[Request.METHOD_CONNECT],
client.connect(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
public void patch() throws Exception {
assertEquals(
Integer.toString(Request.METHOD_PATCH),
client.patch(ENDPOINT).getBodyUtf8());
Request.METHODS[Request.METHOD_PATCH],
client.patch(ECHO_METHOD_ENDPOINT).getBodyUtf8());
}

@Test
@@ -155,12 +163,12 @@ public void malformed() throws Exception {
super(config);
}

@Path(ENDPOINT)
public Response echoMethod(Request request) throws IOException {
return Response.ok(Integer.toString(request.getMethod()));
@Path(ECHO_METHOD_ENDPOINT)
public Response echoMethod(Request request) {
return Response.ok(request.getMethodName());
}

@Path(ENDPOINT)
@Path(ECHO_PUT_ENDPOINT)
@RequestMethod(Request.METHOD_PUT)
public Response echoMethodOnPut(Request request) {
return Response.ok("echoMethodOnPut");

0 comments on commit 2b10057

Please sign in to comment.
You can’t perform that action at this time.