diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 7eeb7fb54a..9026410a45 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -45,11 +45,6 @@ hpack 0.6.0 - - com.google.guava - guava - 16.0.1 - org.mockito mockito-all diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/DefaultHttp2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/DefaultHttp2Headers.java new file mode 100644 index 0000000000..7fda494f08 --- /dev/null +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/DefaultHttp2Headers.java @@ -0,0 +1,513 @@ +/* + * Copyright 2014 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.http2.draft10; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeSet; + +/** + * An immutable collection of headers sent or received via HTTP/2. + */ +public final class DefaultHttp2Headers extends Http2Headers { + private static final int MAX_VALUE_LENGTH = 0xFFFF; // Length is a 16-bit field + private static final int BUCKET_SIZE = 17; + + private final HeaderEntry[] entries; + private final HeaderEntry head; + + private DefaultHttp2Headers(Builder builder) { + this.entries = builder.entries; + this.head = builder.head; + } + + @Override + public String get(String name) { + if (name == null) { + throw new NullPointerException("name"); + } + + int h = hash(name); + int i = index(h); + HeaderEntry e = entries[i]; + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + return e.value; + } + + e = e.next; + } + return null; + } + + @Override + public List getAll(String name) { + if (name == null) { + throw new NullPointerException("name"); + } + + LinkedList values = new LinkedList(); + + int h = hash(name); + int i = index(h); + HeaderEntry e = entries[i]; + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + values.addFirst(e.value); + } + e = e.next; + } + return values; + } + + @Override + public List> entries() { + List> all = + new LinkedList>(); + + HeaderEntry e = head.after; + while (e != head) { + all.add(e); + e = e.after; + } + return all; + } + + @Override + public boolean contains(String name) { + return get(name) != null; + } + + @Override + public boolean isEmpty() { + return head == head.after; + } + + @Override + public Set names() { + Set names = new TreeSet(); + + HeaderEntry e = head.after; + while (e != head) { + names.add(e.key); + e = e.after; + } + return names; + } + + @Override + public Iterator> iterator() { + return new HeaderIterator(); + } + + /** + * Short cut for {@code new DefaultHttp2Headers.Builder()}. + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Builds instances of {@link DefaultHttp2Header}. + */ + public static class Builder { + private HeaderEntry[] entries; + private HeaderEntry head; + private Http2Headers buildResults; + + public Builder() { + clear(); + } + + public void set(Http2Headers headers) { + // No need to lazy copy the previous results, since we're starting from scratch. + clear(); + for (Map.Entry entry : headers) { + add(entry.getKey(), entry.getValue()); + } + } + + public Builder add(final String name, final Object value) { + // If this is the first call on the builder since the last build, copy the previous + // results. + lazyCopy(); + + String lowerCaseName = name.toLowerCase(); + validateHeaderName(lowerCaseName); + String strVal = toString(value); + validateHeaderValue(strVal); + int nameHash = hash(lowerCaseName); + int hashTableIndex = index(nameHash); + add0(nameHash, hashTableIndex, lowerCaseName, strVal); + return this; + } + + public Builder remove(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + + // If this is the first call on the builder since the last build, copy the previous + // results. + lazyCopy(); + + String lowerCaseName = name.toLowerCase(); + int nameHash = hash(lowerCaseName); + int hashTableIndex = index(nameHash); + remove0(nameHash, hashTableIndex, lowerCaseName); + return this; + } + + public Builder set(final String name, final Object value) { + // If this is the first call on the builder since the last build, copy the previous + // results. + lazyCopy(); + + String lowerCaseName = name.toLowerCase(); + validateHeaderName(lowerCaseName); + String strVal = toString(value); + validateHeaderValue(strVal); + int nameHash = hash(lowerCaseName); + int hashTableIndex = index(nameHash); + remove0(nameHash, hashTableIndex, lowerCaseName); + add0(nameHash, hashTableIndex, lowerCaseName, strVal); + return this; + } + + public Builder set(final String name, final Iterable values) { + if (values == null) { + throw new NullPointerException("values"); + } + + // If this is the first call on the builder since the last build, copy the previous + // results. + lazyCopy(); + + String lowerCaseName = name.toLowerCase(); + validateHeaderName(lowerCaseName); + + int nameHash = hash(lowerCaseName); + int hashTableIndex = index(nameHash); + + remove0(nameHash, hashTableIndex, lowerCaseName); + for (Object v: values) { + if (v == null) { + break; + } + String strVal = toString(v); + validateHeaderValue(strVal); + add0(nameHash, hashTableIndex, lowerCaseName, strVal); + } + return this; + } + + public Builder clear() { + // No lazy copy required, since we're just creating a new array. + entries = new HeaderEntry[BUCKET_SIZE]; + head = new HeaderEntry(-1, null, null); + head.before = head.after = head; + buildResults = null; + return this; + } + + /** + * Sets the {@link HttpName#METHOD} header. + */ + public Builder setMethod(String method) { + return set(HttpName.METHOD.value(), method); + } + + /** + * Sets the {@link HttpName#SCHEME} header. + */ + public Builder setScheme(String scheme) { + return set(HttpName.SCHEME.value(), scheme); + } + + /** + * Sets the {@link HttpName#AUTHORITY} header. + */ + public Builder setAuthority(String authority) { + return set(HttpName.AUTHORITY.value(), authority); + } + + /** + * Sets the {@link HttpName#PATH} header. + */ + public Builder setPath(String path) { + return set(HttpName.PATH.value(), path); + } + + /** + * Sets the {@link HttpName#STATUS} header. + */ + public Builder setStatus(String status) { + return set(HttpName.STATUS.value(), status); + } + + /** + * Builds a new instance of {@link DefaultHttp2Headers}. + */ + public DefaultHttp2Headers build() { + // If this is the first call on the builder since the last build, copy the previous + // results. + lazyCopy(); + + // Give the multimap over to the headers instance and save the build results for + // future lazy copies if this builder is used again later. + DefaultHttp2Headers headers = new DefaultHttp2Headers(this); + buildResults = headers; + return headers; + } + + /** + * Performs a lazy copy of the last build results, if there are any. For the typical use + * case, headers will only be built once so no copy will be required. If the any method + * is called on the builder after that, it will force a copy of the most recently created + * headers object. + */ + private void lazyCopy() { + if (buildResults != null) { + set(buildResults); + buildResults = null; + } + } + + private void add0(int hash, int hashTableIndex, final String name, final String value) { + // Update the hash table. + HeaderEntry e = entries[hashTableIndex]; + HeaderEntry newEntry; + entries[hashTableIndex] = newEntry = new HeaderEntry(hash, name, value); + newEntry.next = e; + + // Update the linked list. + newEntry.addBefore(head); + } + + private void remove0(int hash, int hashTableIndex, String name) { + HeaderEntry e = entries[hashTableIndex]; + if (e == null) { + return; + } + + for (;;) { + if (e.hash == hash && eq(name, e.key)) { + e.remove(); + HeaderEntry next = e.next; + if (next != null) { + entries[hashTableIndex] = next; + e = next; + } else { + entries[hashTableIndex] = null; + return; + } + } else { + break; + } + } + + for (;;) { + HeaderEntry next = e.next; + if (next == null) { + break; + } + if (next.hash == hash && eq(name, next.key)) { + e.next = next.next; + next.remove(); + } else { + e = next; + } + } + } + + private static String toString(Object value) { + if (value == null) { + return null; + } + return value.toString(); + } + } + + private static int hash(String name) { + int h = 0; + for (int i = name.length() - 1; i >= 0; i --) { + char c = name.charAt(i); + if (c >= 'A' && c <= 'Z') { + c += 32; + } + h = 31 * h + c; + } + + if (h > 0) { + return h; + } else if (h == Integer.MIN_VALUE) { + return Integer.MAX_VALUE; + } else { + return -h; + } + } + + private static boolean eq(String name1, String name2) { + int nameLen = name1.length(); + if (nameLen != name2.length()) { + return false; + } + + for (int i = nameLen - 1; i >= 0; i --) { + char c1 = name1.charAt(i); + char c2 = name2.charAt(i); + if (c1 != c2) { + if (c1 >= 'A' && c1 <= 'Z') { + c1 += 32; + } + if (c2 >= 'A' && c2 <= 'Z') { + c2 += 32; + } + if (c1 != c2) { + return false; + } + } + } + return true; + } + + private static int index(int hash) { + return hash % BUCKET_SIZE; + } + + /** + * Validate a HTTP2 header name. + */ + private static void validateHeaderName(String name) { + if (name == null) { + throw new NullPointerException("name"); + } + if (name.isEmpty()) { + throw new IllegalArgumentException( + "name cannot be length zero"); + } + // Since name may only contain ascii characters, for valid names + // name.length() returns the number of bytes when UTF-8 encoded. + if (name.length() > MAX_VALUE_LENGTH) { + throw new IllegalArgumentException( + "name exceeds allowable length: " + name); + } + for (int i = 0; i < name.length(); i ++) { + char c = name.charAt(i); + if (c == 0) { + throw new IllegalArgumentException( + "name contains null character: " + name); + } + if (c > 127) { + throw new IllegalArgumentException( + "name contains non-ascii character: " + name); + } + } + } + + /** + * Validate a HTTP2 header value. Does not validate max length. + */ + private static void validateHeaderValue(String value) { + if (value == null) { + throw new NullPointerException("value"); + } + for (int i = 0; i < value.length(); i ++) { + char c = value.charAt(i); + if (c == 0) { + throw new IllegalArgumentException( + "value contains null character: " + value); + } + } + } + + private final class HeaderIterator implements Iterator> { + + private HeaderEntry current = head; + + @Override + public boolean hasNext() { + return current.after != head; + } + + @Override + public Entry next() { + current = current.after; + + if (current == head) { + throw new NoSuchElementException(); + } + + return current; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + private static final class HeaderEntry implements Map.Entry { + final int hash; + final String key; + final String value; + HeaderEntry next; + HeaderEntry before, after; + + HeaderEntry(int hash, String key, String value) { + this.hash = hash; + this.key = key; + this.value = value; + } + + void remove() { + before.after = after; + after.before = before; + } + + void addBefore(HeaderEntry e) { + after = e; + before = e.before; + before.after = this; + after.before = this; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return key + '=' + value; + } + } +} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/Http2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/Http2Headers.java index 65826c4b76..478c13a7da 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/Http2Headers.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/Http2Headers.java @@ -15,14 +15,57 @@ package io.netty.handler.codec.http2.draft10; +import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; import java.util.Map.Entry; +import java.util.Set; -import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableCollection; -import com.google.common.collect.ImmutableMultimap; +/** + * An immutable collection of headers sent or received via HTTP/2. + */ +public abstract class Http2Headers implements Iterable> { -public final class Http2Headers implements Iterable> { + public static final Http2Headers EMPTY_HEADERS = new Http2Headers() { + + @Override + public String get(String name) { + return null; + } + + @Override + public List getAll(String name) { + return Collections.emptyList(); + } + + @Override + public List> entries() { + return Collections.emptyList(); + } + + @Override + public boolean contains(String name) { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Set names() { + return Collections.emptySet(); + } + + @Override + public Iterator> iterator() { + return entries().iterator(); + } + }; /** * HTTP2 header names. @@ -64,130 +107,136 @@ public final class Http2Headers implements Iterable> { } } - private final ImmutableMultimap headers; + /** + * Returns the {@link Set} of all header names. + */ + public abstract Set names(); - private Http2Headers(Builder builder) { - this.headers = builder.map.build(); + /** + * Returns the header value with the specified header name. If there is + * more than one header value for the specified header name, the first + * value is returned. + * + * @return the header value or {@code null} if there is no such header + */ + public abstract String get(String name); + + /** + * Returns the header values with the specified header name. + * + * @return the {@link List} of header values. An empty list if there is no + * such header. + */ + public abstract List getAll(String name); + + /** + * Returns all header names and values that this frame contains. + * + * @return the {@link List} of the header name-value pairs. An empty list + * if there is no header in this message. + */ + public abstract List> entries(); + + /** + * Returns {@code true} if and only if there is a header with the specified + * header name. + */ + public abstract boolean contains(String name); + + /** + * Checks if no header exists. + */ + public abstract boolean isEmpty(); + + /** + * Gets the {@link HttpName#METHOD} header. + * + * @return the header value or {@code null} if there is no such header + */ + public final String getMethod() { + return get(HttpName.METHOD.value()); } - public String getHeader(String name) { - ImmutableCollection col = getHeaders(name); - return col.isEmpty() ? null : col.iterator().next(); + /** + * Gets the {@link HttpName#SCHEME} header. + * + * @return the header value or {@code null} if there is no such header + */ + public final String getScheme() { + return get(HttpName.SCHEME.value()); } - public ImmutableCollection getHeaders(String name) { - return headers.get(name); + /** + * Gets the {@link HttpName#AUTHORITY} header. + * + * @return the header value or {@code null} if there is no such header + */ + public final String getAuthority() { + return get(HttpName.AUTHORITY.value()); } - public String getMethod() { - return getHeader(HttpName.METHOD.value()); + /** + * Gets the {@link HttpName#PATH} header. + * + * @return the header value or {@code null} if there is no such header + */ + public final String getPath() { + return get(HttpName.PATH.value()); } - public String getScheme() { - return getHeader(HttpName.SCHEME.value()); - } - - public String getAuthority() { - return getHeader(HttpName.AUTHORITY.value()); - } - - public String getPath() { - return getHeader(HttpName.PATH.value()); - } - - public String getStatus() { - return getHeader(HttpName.STATUS.value()); - } - - @Override - public Iterator> iterator() { - return headers.entries().iterator(); + /** + * Gets the {@link HttpName#STATUS} header. + * + * @return the header value or {@code null} if there is no such header + */ + public final String getStatus() { + return get(HttpName.STATUS.value()); } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((headers == null) ? 0 : headers.hashCode()); + for (String name : names()) { + result = prime * result + name.hashCode(); + Set values = new TreeSet(getAll(name)); + for (String value : values) { + result = prime * result + value.hashCode(); + } + } return result; } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { + public boolean equals(Object o) { + if (!(o instanceof Http2Headers)) { return false; } - if (getClass() != obj.getClass()) { + Http2Headers other = (Http2Headers) o; + + // First, check that the set of names match. + Set names = names(); + if (!names.equals(other.names())) { return false; } - Http2Headers other = (Http2Headers) obj; - if (headers == null) { - if (other.headers != null) { + + // Compare the values for each name. + for (String name : names) { + List values = getAll(name); + List otherValues = other.getAll(name); + if (values.size() != otherValues.size()) { + return false; + } + // Convert the values to a set and remove values from the other object to see if + // they match. + Set valueSet = new HashSet(values); + valueSet.removeAll(otherValues); + if (!valueSet.isEmpty()) { return false; } - } else if (!headers.equals(other.headers)) { - return false; } + + // They match. return true; } - - @Override - public String toString() { - return headers.toString(); - } - - public static class Builder { - private ImmutableMultimap.Builder map = ImmutableMultimap.builder(); - - public Builder clear() { - map = ImmutableMultimap.builder(); - return this; - } - - public Builder addHeaders(Http2Headers headers) { - if (headers == null) { - throw new IllegalArgumentException("headers must not be null."); - } - map.putAll(headers.headers); - return this; - } - - public Builder addHeader(String name, String value) { - // Use interning on the header name to save space. - map.put(name.intern(), value); - return this; - } - - public Builder addHeader(byte[] name, byte[] value) { - addHeader(new String(name, Charsets.UTF_8), new String(value, Charsets.UTF_8)); - return this; - } - - public Builder setMethod(String value) { - return addHeader(HttpName.METHOD.value(), value); - } - - public Builder setScheme(String value) { - return addHeader(HttpName.SCHEME.value(), value); - } - - public Builder setAuthority(String value) { - return addHeader(HttpName.AUTHORITY.value(), value); - } - - public Builder setPath(String value) { - return addHeader(HttpName.PATH.value(), value); - } - - public Builder setStatus(String value) { - return addHeader(HttpName.STATUS.value(), value); - } - - public Http2Headers build() { - return new Http2Headers(this); - } - } } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultHttp2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultHttp2Connection.java index 94af51acb4..97b7c79755 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultHttp2Connection.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultHttp2Connection.java @@ -20,7 +20,6 @@ import static io.netty.handler.codec.http2.draft10.Http2Exception.format; import static io.netty.handler.codec.http2.draft10.Http2Exception.protocolError; import static io.netty.handler.codec.http2.draft10.connection.Http2ConnectionUtil.toByteBuf; import static io.netty.handler.codec.http2.draft10.frame.Http2FrameCodecUtil.DEFAULT_STREAM_PRIORITY; - import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -32,23 +31,37 @@ import io.netty.handler.codec.http2.draft10.connection.Http2Stream.State; import io.netty.handler.codec.http2.draft10.frame.DefaultHttp2GoAwayFrame; import io.netty.handler.codec.http2.draft10.frame.Http2GoAwayFrame; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multiset; -import com.google.common.collect.TreeMultiset; +import java.util.Set; +import java.util.TreeSet; public class DefaultHttp2Connection implements Http2Connection { + /** + * Used to sort streams in the activeStreams set. Sort by priority first, then by stream ID. + * Streams with the same ID are considered equal. + */ + private static final Comparator STREAM_COMPARATOR = new Comparator() { + @Override + public int compare(Http2Stream s1, Http2Stream s2) { + int p1 = s1.getPriority(); + int p2 = s2.getPriority(); - private final List listeners = Lists.newArrayList(); - private final Map streamMap = Maps.newHashMap(); - private final Multiset activeStreams = TreeMultiset.create(); + // Sort streams with the same priority by their ID. + if (p1 == p2) { + return s1.getId() - s2.getId(); + } + return p1 - p2; + } + }; + + private final List listeners = new ArrayList(); + private final Map streamMap = new HashMap(); + private final Set activeStreams = new TreeSet(STREAM_COMPARATOR); private final DefaultEndpoint localEndpoint; private final DefaultEndpoint remoteEndpoint; private boolean goAwaySent; @@ -85,10 +98,10 @@ public class DefaultHttp2Connection implements Http2Connection { } @Override - public List getActiveStreams() { + public Set getActiveStreams() { // Copy the list in case any operation on the returned streams causes the activeStreams set // to change. - return ImmutableList.copyOf(activeStreams); + return Collections.unmodifiableSet(activeStreams); } @Override @@ -181,21 +194,14 @@ public class DefaultHttp2Connection implements Http2Connection { return state; } - @Override - public int compareTo(Http2Stream other) { - // Sort streams with the same priority by their ID. - if (priority == other.getPriority()) { - return id - other.getId(); - } - return priority - other.getPriority(); - } - @Override public void verifyState(Http2Error error, State... allowedStates) throws Http2Exception { - Predicate predicate = Predicates.in(Arrays.asList(allowedStates)); - if (!predicate.apply(state)) { - throw format(error, "Stream %d in unexpected state: %s", id, state); + for (State allowedState : allowedStates) { + if (state == allowedState) { + return; + } } + throw format(error, "Stream %d in unexpected state: %s", id, state); } @Override diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultInboundFlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultInboundFlowController.java index 79466074d9..ca29588dcf 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultInboundFlowController.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultInboundFlowController.java @@ -19,16 +19,14 @@ import static io.netty.handler.codec.http2.draft10.Http2Exception.flowControlErr import static io.netty.handler.codec.http2.draft10.Http2Exception.protocolError; import static io.netty.handler.codec.http2.draft10.connection.Http2ConnectionUtil.DEFAULT_FLOW_CONTROL_WINDOW_SIZE; import static io.netty.handler.codec.http2.draft10.frame.Http2FrameCodecUtil.CONNECTION_STREAM_ID; - import io.netty.handler.codec.http2.draft10.Http2Exception; import io.netty.handler.codec.http2.draft10.frame.DefaultHttp2WindowUpdateFrame; import io.netty.handler.codec.http2.draft10.frame.Http2DataFrame; import io.netty.handler.codec.http2.draft10.frame.Http2WindowUpdateFrame; +import java.util.HashMap; import java.util.Map; -import com.google.common.collect.Maps; - /** * Basic implementation of {@link InboundFlowController}. */ @@ -36,7 +34,7 @@ public class DefaultInboundFlowController implements InboundFlowController { private int initialWindowSize = DEFAULT_FLOW_CONTROL_WINDOW_SIZE; private final StreamWindow connectionWindow = new StreamWindow(CONNECTION_STREAM_ID); - private final Map streamWindows = Maps.newHashMap(); + private final Map streamWindows = new HashMap(); public DefaultInboundFlowController(Http2Connection connection) { connection.addListener(new Http2Connection.Listener() { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultOutboundFlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultOutboundFlowController.java index 5d458f1be0..031dc27bc7 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultOutboundFlowController.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/DefaultOutboundFlowController.java @@ -21,25 +21,23 @@ import static io.netty.handler.codec.http2.draft10.Http2Exception.format; import static io.netty.handler.codec.http2.draft10.Http2Exception.protocolError; import static io.netty.handler.codec.http2.draft10.connection.Http2ConnectionUtil.DEFAULT_FLOW_CONTROL_WINDOW_SIZE; import static io.netty.handler.codec.http2.draft10.frame.Http2FrameCodecUtil.CONNECTION_STREAM_ID; - import io.netty.handler.codec.http2.draft10.Http2Exception; import io.netty.handler.codec.http2.draft10.Http2StreamException; import io.netty.handler.codec.http2.draft10.frame.DefaultHttp2DataFrame; import io.netty.handler.codec.http2.draft10.frame.Http2DataFrame; +import java.util.ArrayDeque; +import java.util.HashMap; import java.util.Map; import java.util.Queue; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - /** * Basic implementation of {@link OutboundFlowController}. */ public class DefaultOutboundFlowController implements OutboundFlowController { private final Http2Connection connection; - private final Map streamStates = Maps.newHashMap(); + private final Map streamStates = new HashMap(); private int initialWindowSize = DEFAULT_FLOW_CONTROL_WINDOW_SIZE; private int connectionWindowSize = DEFAULT_FLOW_CONTROL_WINDOW_SIZE; @@ -185,7 +183,7 @@ public class DefaultOutboundFlowController implements OutboundFlowController { */ private class StreamState { private final int streamId; - private final Queue pendingWriteQueue = Lists.newLinkedList(); + private final Queue pendingWriteQueue = new ArrayDeque(); private int windowSize = initialWindowSize; public StreamState(int streamId) { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/Http2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/Http2Connection.java index 28a0517583..b5a4e7e89a 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/Http2Connection.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/Http2Connection.java @@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.http2.draft10.Http2Exception; -import java.util.List; +import java.util.Collection; public interface Http2Connection { @@ -132,7 +132,7 @@ public interface Http2Connection { * Gets all streams that are currently either open or half-closed. The returned collection is * sorted by priority. */ - List getActiveStreams(); + Collection getActiveStreams(); /** * Gets a view of this connection from the local {@link Endpoint}. diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/Http2Stream.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/Http2Stream.java index 7fe5f68e9c..1f481f7fb6 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/Http2Stream.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/connection/Http2Stream.java @@ -23,7 +23,7 @@ import io.netty.handler.codec.http2.draft10.Http2Exception; /** * A single stream within an HTTP2 connection. Streams are compared to each other by priority. */ -public interface Http2Stream extends Comparable { +public interface Http2Stream { /** * The allowed states of an HTTP2 stream. diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/DefaultHttp2HeadersFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/DefaultHttp2HeadersFrame.java index f4133ae810..d5b5c0c759 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/DefaultHttp2HeadersFrame.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/DefaultHttp2HeadersFrame.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http2.draft10.frame; import static io.netty.handler.codec.http2.draft10.frame.Http2FrameCodecUtil.DEFAULT_STREAM_PRIORITY; - +import io.netty.handler.codec.http2.draft10.DefaultHttp2Headers; import io.netty.handler.codec.http2.draft10.Http2Headers; public final class DefaultHttp2HeadersFrame implements Http2HeadersFrame { @@ -104,7 +104,7 @@ public final class DefaultHttp2HeadersFrame implements Http2HeadersFrame { public static class Builder { private int streamId; private int priority = DEFAULT_STREAM_PRIORITY; - private final Http2Headers.Builder headersBuilder = new Http2Headers.Builder(); + private final DefaultHttp2Headers.Builder headersBuilder = new DefaultHttp2Headers.Builder(); private boolean endOfStream; public Builder setStreamId(int streamId) { @@ -128,12 +128,12 @@ public final class DefaultHttp2HeadersFrame implements Http2HeadersFrame { return this; } - public Http2Headers.Builder headers() { + public DefaultHttp2Headers.Builder headers() { return headersBuilder; } public Builder setHeaders(Http2Headers headers) { - headersBuilder.addHeaders(headers); + headersBuilder.set(headers); return this; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/DefaultHttp2HeadersDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/DefaultHttp2HeadersDecoder.java index d231296945..5775c5c288 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/DefaultHttp2HeadersDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/DefaultHttp2HeadersDecoder.java @@ -17,12 +17,13 @@ package io.netty.handler.codec.http2.draft10.frame.decoder; import static io.netty.handler.codec.http2.draft10.connection.Http2ConnectionUtil.DEFAULT_HEADER_TABLE_SIZE; import static io.netty.handler.codec.http2.draft10.connection.Http2ConnectionUtil.DEFAULT_MAX_HEADER_SIZE; - import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; +import io.netty.handler.codec.http2.draft10.DefaultHttp2Headers; import io.netty.handler.codec.http2.draft10.Http2Error; import io.netty.handler.codec.http2.draft10.Http2Exception; import io.netty.handler.codec.http2.draft10.Http2Headers; +import io.netty.util.CharsetUtil; import java.io.IOException; @@ -45,11 +46,12 @@ public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder { @Override public Http2Headers decodeHeaders(ByteBuf headerBlock) throws Http2Exception { try { - final Http2Headers.Builder headersBuilder = new Http2Headers.Builder(); + final DefaultHttp2Headers.Builder headersBuilder = new DefaultHttp2Headers.Builder(); HeaderListener listener = new HeaderListener() { @Override public void emitHeader(byte[] key, byte[] value) { - headersBuilder.addHeader(key, value); + headersBuilder.add(new String(key, CharsetUtil.UTF_8), new String(value, + CharsetUtil.UTF_8)); } }; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/Http2HeadersFrameUnmarshaller.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/Http2HeadersFrameUnmarshaller.java index f17c1a2c03..5beff990a6 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/Http2HeadersFrameUnmarshaller.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/Http2HeadersFrameUnmarshaller.java @@ -31,8 +31,6 @@ import io.netty.handler.codec.http2.draft10.frame.Http2Frame; import io.netty.handler.codec.http2.draft10.frame.Http2FrameHeader; import io.netty.handler.codec.http2.draft10.frame.Http2HeadersFrame; -import com.google.common.base.Preconditions; - /** * An unmarshaller for {@link Http2HeadersFrame} instances. */ @@ -41,7 +39,10 @@ public class Http2HeadersFrameUnmarshaller extends AbstractHeadersUnmarshaller { private final Http2HeadersDecoder headersDecoder; public Http2HeadersFrameUnmarshaller(Http2HeadersDecoder headersDecoder) { - this.headersDecoder = Preconditions.checkNotNull(headersDecoder, "headersDecoder"); + if (headersDecoder == null) { + throw new IllegalArgumentException("headersDecoder must not be null."); + } + this.headersDecoder = headersDecoder; } @Override diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/Http2PushPromiseFrameUnmarshaller.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/Http2PushPromiseFrameUnmarshaller.java index 5c13fec8cf..4080603f43 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/Http2PushPromiseFrameUnmarshaller.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/decoder/Http2PushPromiseFrameUnmarshaller.java @@ -30,8 +30,6 @@ import io.netty.handler.codec.http2.draft10.frame.Http2Frame; import io.netty.handler.codec.http2.draft10.frame.Http2FrameHeader; import io.netty.handler.codec.http2.draft10.frame.Http2PushPromiseFrame; -import com.google.common.base.Preconditions; - /** * An unmarshaller for {@link Http2PushPromiseFrame} instances. */ @@ -40,7 +38,10 @@ public class Http2PushPromiseFrameUnmarshaller extends AbstractHeadersUnmarshall private final Http2HeadersDecoder headersDecoder; public Http2PushPromiseFrameUnmarshaller(Http2HeadersDecoder headersDecoder) { - this.headersDecoder = Preconditions.checkNotNull(headersDecoder, "headersDecoder"); + if (headersDecoder == null) { + throw new IllegalArgumentException("headersDecoder must not be null."); + } + this.headersDecoder = headersDecoder; } @Override diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/DefaultHttp2HeadersEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/DefaultHttp2HeadersEncoder.java index 4331c16475..2c63d337a6 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/DefaultHttp2HeadersEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/DefaultHttp2HeadersEncoder.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http2.draft10.frame.encoder; import static io.netty.handler.codec.http2.draft10.connection.Http2ConnectionUtil.DEFAULT_HEADER_TABLE_SIZE; - +import static io.netty.util.CharsetUtil.UTF_8; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; import io.netty.handler.codec.http2.draft10.Http2Error; @@ -25,14 +25,11 @@ import io.netty.handler.codec.http2.draft10.Http2Headers; import java.io.IOException; import java.io.OutputStream; -import java.nio.charset.Charset; import java.util.Map.Entry; -import com.google.common.base.Charsets; import com.twitter.hpack.Encoder; public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder { - private static final Charset DEFAULT_CHARSET = Charsets.UTF_8; private final Encoder encoder; @@ -45,8 +42,8 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder { try { OutputStream stream = new ByteBufOutputStream(buffer); for (Entry header : headers) { - byte[] key = header.getKey().getBytes(DEFAULT_CHARSET); - byte[] value = header.getValue().getBytes(DEFAULT_CHARSET); + byte[] key = header.getKey().getBytes(UTF_8); + byte[] value = header.getValue().getBytes(UTF_8); encoder.encodeHeader(stream, key, value); } encoder.endHeaders(stream); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/Http2HeadersFrameMarshaller.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/Http2HeadersFrameMarshaller.java index 1d6474c4af..7fb90973d2 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/Http2HeadersFrameMarshaller.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/Http2HeadersFrameMarshaller.java @@ -29,15 +29,16 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.handler.codec.http2.draft10.Http2Exception; import io.netty.handler.codec.http2.draft10.frame.Http2HeadersFrame; -import com.google.common.base.Preconditions; - public class Http2HeadersFrameMarshaller extends AbstractHttp2FrameMarshaller { private final Http2HeadersEncoder headersEncoder; public Http2HeadersFrameMarshaller(Http2HeadersEncoder headersEncoder) { super(Http2HeadersFrame.class); - this.headersEncoder = Preconditions.checkNotNull(headersEncoder, "headersEncoder"); + if (headersEncoder == null) { + throw new NullPointerException("headersEncoder must not be null."); + } + this.headersEncoder = headersEncoder; } @Override diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/Http2PushPromiseFrameMarshaller.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/Http2PushPromiseFrameMarshaller.java index 21de2f1160..78cb7ad9ba 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/Http2PushPromiseFrameMarshaller.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/draft10/frame/encoder/Http2PushPromiseFrameMarshaller.java @@ -26,8 +26,6 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.handler.codec.http2.draft10.Http2Exception; import io.netty.handler.codec.http2.draft10.frame.Http2PushPromiseFrame; -import com.google.common.base.Preconditions; - public class Http2PushPromiseFrameMarshaller extends AbstractHttp2FrameMarshaller { @@ -35,7 +33,10 @@ public class Http2PushPromiseFrameMarshaller extends public Http2PushPromiseFrameMarshaller(Http2HeadersEncoder headersEncoder) { super(Http2PushPromiseFrame.class); - this.headersEncoder = Preconditions.checkNotNull(headersEncoder, "headersEncoder"); + if (headersEncoder == null) { + throw new NullPointerException("headersEncoder must not be null."); + } + this.headersEncoder = headersEncoder; } @Override diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/DefaultHttp2HeadersTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/DefaultHttp2HeadersTest.java new file mode 100644 index 0000000000..4ed9b9ee16 --- /dev/null +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/DefaultHttp2HeadersTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2014 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.http2.draft10; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.junit.Test; + + +/** + * Tests for {@link DefaultHttp2Headers}. + */ +public class DefaultHttp2HeadersTest { + + @Test + public void duplicateKeysShouldStoreAllValues() { + DefaultHttp2Headers headers = + DefaultHttp2Headers.newBuilder().add("a", "1").add("a", "2") + .add("a", "3").build(); + List aValues = headers.getAll("a"); + assertEquals(3, aValues.size()); + assertEquals("1", aValues.get(0)); + assertEquals("2", aValues.get(1)); + assertEquals("3", aValues.get(2)); + } + + @Test(expected = NoSuchElementException.class) + public void iterateEmptyHeadersShouldThrow() { + Iterator> iterator = + DefaultHttp2Headers.newBuilder().build().iterator(); + assertFalse(iterator.hasNext()); + iterator.next(); + } + + @Test + public void iterateHeadersShouldReturnAllValues() { + Set headers = new HashSet(); + headers.add("a:1"); + headers.add("a:2"); + headers.add("a:3"); + headers.add("b:1"); + headers.add("b:2"); + headers.add("c:1"); + + // Build the headers from the input set. + DefaultHttp2Headers.Builder builder = DefaultHttp2Headers.newBuilder(); + for (String header : headers) { + String[] parts = header.split(":"); + builder.add(parts[0], parts[1]); + } + + // Now iterate through the headers, removing them from the original set. + for (Map.Entry entry : builder.build()) { + assertTrue(headers.remove(entry.getKey() + ":" + entry.getValue())); + } + + // Make sure we removed them all. + assertTrue(headers.isEmpty()); + } +} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/DefaultHttp2ConnectionTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/DefaultHttp2ConnectionTest.java index 6787c374e5..8ebd2d747f 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/DefaultHttp2ConnectionTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/DefaultHttp2ConnectionTest.java @@ -32,6 +32,7 @@ import io.netty.handler.codec.http2.draft10.Http2Exception; import io.netty.handler.codec.http2.draft10.connection.Http2Connection.Listener; import io.netty.handler.codec.http2.draft10.connection.Http2Stream.State; +import java.util.ArrayList; import java.util.List; import org.junit.Before; @@ -225,7 +226,7 @@ public class DefaultHttp2ConnectionTest { server.local().createStream(4, 256, true); server.remote().createStream(3, Integer.MAX_VALUE, true); server.remote().createStream(5, 1, false); - List activeStreams = server.getActiveStreams(); + List activeStreams = activeStreams(); assertEquals(2, activeStreams.get(0).getId()); assertEquals(5, activeStreams.get(1).getId()); assertEquals(4, activeStreams.get(2).getId()); @@ -244,7 +245,7 @@ public class DefaultHttp2ConnectionTest { // Make this this highest priority. stream7.setPriority(0); - List activeStreams = server.getActiveStreams(); + List activeStreams = activeStreams(); assertEquals(7, activeStreams.get(0).getId()); assertEquals(2, activeStreams.get(1).getId()); assertEquals(5, activeStreams.get(2).getId()); @@ -305,4 +306,8 @@ public class DefaultHttp2ConnectionTest { stream.close(ctx, future); verify(ctx).close(promise); } + + private List activeStreams() { + return new ArrayList(server.getActiveStreams()); + } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/DefaultOutboundFlowControllerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/DefaultOutboundFlowControllerTest.java index 242e8b8eb6..c1889549d7 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/DefaultOutboundFlowControllerTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/DefaultOutboundFlowControllerTest.java @@ -23,6 +23,9 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; + +import java.util.Arrays; + import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http2.draft10.Http2Exception; @@ -39,8 +42,6 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import com.google.common.collect.ImmutableList; - /** * Tests for {@link DefaultOutboundFlowController}. */ @@ -74,7 +75,7 @@ public class DefaultOutboundFlowControllerTest { return null; } }).when(connection).addListener(any(Listener.class)); - when(connection.getActiveStreams()).thenReturn(ImmutableList.of(stream)); + when(connection.getActiveStreams()).thenReturn(Arrays.asList(stream)); when(stream.getId()).thenReturn(STREAM_ID); controller = new DefaultOutboundFlowController(connection); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/Http2ConnectionHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/Http2ConnectionHandlerTest.java index 4632381e33..00c4d36d1b 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/Http2ConnectionHandlerTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/connection/Http2ConnectionHandlerTest.java @@ -17,6 +17,7 @@ package io.netty.handler.codec.http2.draft10.connection; import static io.netty.handler.codec.http2.draft10.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.draft10.frame.Http2FrameCodecUtil.PING_FRAME_PAYLOAD_LENGTH; +import static io.netty.util.CharsetUtil.UTF_8; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; @@ -51,15 +52,13 @@ import io.netty.handler.codec.http2.draft10.frame.Http2Frame; import io.netty.handler.codec.http2.draft10.frame.Http2PingFrame; import io.netty.handler.codec.http2.draft10.frame.Http2SettingsFrame; -import java.nio.charset.Charset; +import java.util.Arrays; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import com.google.common.collect.ImmutableList; - /** * Tests for {@link Http2ConnectionHandler}. */ @@ -111,7 +110,7 @@ public class Http2ConnectionHandlerTest { when(channel.isActive()).thenReturn(true); when(stream.getId()).thenReturn(STREAM_ID); when(pushStream.getId()).thenReturn(PUSH_STREAM_ID); - when(connection.getActiveStreams()).thenReturn(ImmutableList.of(stream)); + when(connection.getActiveStreams()).thenReturn(Arrays.asList(stream)); when(connection.getStream(STREAM_ID)).thenReturn(stream); when(connection.getStreamOrFail(STREAM_ID)).thenReturn(stream); when(connection.local()).thenReturn(local); @@ -157,7 +156,7 @@ public class Http2ConnectionHandlerTest { @Test public void inboundDataAfterGoAwayShouldApplyFlowControl() throws Exception { when(connection.isGoAwaySent()).thenReturn(true); - ByteBuf data = Unpooled.copiedBuffer("Hello", Charset.defaultCharset()); + ByteBuf data = Unpooled.copiedBuffer("Hello", UTF_8); Http2DataFrame frame = new DefaultHttp2DataFrame.Builder().setStreamId(STREAM_ID).setContent(data).build(); handler.channelRead(ctx, frame); @@ -168,7 +167,7 @@ public class Http2ConnectionHandlerTest { @Test public void inboundDataWithEndOfStreamShouldCloseRemoteSide() throws Exception { - ByteBuf data = Unpooled.copiedBuffer("Hello", Charset.defaultCharset()); + ByteBuf data = Unpooled.copiedBuffer("Hello", UTF_8); Http2DataFrame frame = new DefaultHttp2DataFrame.Builder().setStreamId(STREAM_ID).setEndOfStream(true) .setContent(data).build(); @@ -182,7 +181,7 @@ public class Http2ConnectionHandlerTest { @Test public void inboundDataShouldSucceed() throws Exception { - ByteBuf data = Unpooled.copiedBuffer("Hello", Charset.defaultCharset()); + ByteBuf data = Unpooled.copiedBuffer("Hello", UTF_8); Http2DataFrame frame = new DefaultHttp2DataFrame.Builder().setStreamId(STREAM_ID).setContent(data).build(); handler.channelRead(ctx, frame); @@ -198,7 +197,7 @@ public class Http2ConnectionHandlerTest { when(connection.isGoAwaySent()).thenReturn(true); Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(STREAM_ID).setPriority(1) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.channelRead(ctx, frame); verify(remote, never()).createStream(eq(STREAM_ID), eq(1), eq(false)); verify(ctx, never()).fireChannelRead(frame); @@ -209,7 +208,7 @@ public class Http2ConnectionHandlerTest { int newStreamId = 5; Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(newStreamId).setPriority(1) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.channelRead(ctx, frame); verify(remote).createStream(eq(newStreamId), eq(1), eq(false)); verify(ctx).fireChannelRead(frame); @@ -220,7 +219,7 @@ public class Http2ConnectionHandlerTest { int newStreamId = 5; Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(newStreamId).setPriority(1) - .setEndOfStream(true).setHeaders(new Http2Headers.Builder().build()) + .setEndOfStream(true).setHeaders(Http2Headers.EMPTY_HEADERS) .build(); handler.channelRead(ctx, frame); verify(remote).createStream(eq(newStreamId), eq(1), eq(true)); @@ -231,7 +230,7 @@ public class Http2ConnectionHandlerTest { public void inboundHeadersWithForPromisedStreamShouldHalfOpenStream() throws Exception { Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(STREAM_ID).setPriority(1) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.channelRead(ctx, frame); verify(stream).openForPush(); verify(ctx).fireChannelRead(frame); @@ -241,7 +240,7 @@ public class Http2ConnectionHandlerTest { public void inboundHeadersWithForPromisedStreamShouldCloseStream() throws Exception { Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(STREAM_ID).setPriority(1) - .setEndOfStream(true).setHeaders(new Http2Headers.Builder().build()) + .setEndOfStream(true).setHeaders(Http2Headers.EMPTY_HEADERS) .build(); handler.channelRead(ctx, frame); verify(stream).openForPush(); @@ -255,7 +254,7 @@ public class Http2ConnectionHandlerTest { Http2Frame frame = new DefaultHttp2PushPromiseFrame.Builder().setStreamId(STREAM_ID) .setPromisedStreamId(PUSH_STREAM_ID) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.channelRead(ctx, frame); verify(remote, never()).reservePushStream(eq(PUSH_STREAM_ID), eq(stream)); verify(ctx, never()).fireChannelRead(frame); @@ -266,7 +265,7 @@ public class Http2ConnectionHandlerTest { Http2Frame frame = new DefaultHttp2PushPromiseFrame.Builder().setStreamId(STREAM_ID) .setPromisedStreamId(PUSH_STREAM_ID) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.channelRead(ctx, frame); verify(remote).reservePushStream(eq(PUSH_STREAM_ID), eq(stream)); verify(ctx).fireChannelRead(frame); @@ -419,7 +418,7 @@ public class Http2ConnectionHandlerTest { @Test public void outboundDataAfterGoAwayShouldFail() throws Exception { when(connection.isGoAway()).thenReturn(true); - ByteBuf data = Unpooled.copiedBuffer("Hello", Charset.defaultCharset()); + ByteBuf data = Unpooled.copiedBuffer("Hello", UTF_8); Http2DataFrame frame = new DefaultHttp2DataFrame.Builder().setStreamId(STREAM_ID).setContent(data).build(); handler.write(ctx, frame, promise); @@ -431,7 +430,7 @@ public class Http2ConnectionHandlerTest { @Test public void outboundDataShouldApplyFlowControl() throws Exception { - ByteBuf data = Unpooled.copiedBuffer("Hello", Charset.defaultCharset()); + ByteBuf data = Unpooled.copiedBuffer("Hello", UTF_8); Http2DataFrame frame = new DefaultHttp2DataFrame.Builder().setStreamId(STREAM_ID).setContent(data).build(); handler.write(ctx, frame, promise); @@ -447,7 +446,7 @@ public class Http2ConnectionHandlerTest { when(connection.isGoAway()).thenReturn(true); Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(STREAM_ID).setPriority(1) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.write(ctx, frame, promise); verify(promise).setFailure(any(Http2Exception.class)); verify(ctx, never()).writeAndFlush(frame, promise); @@ -458,7 +457,7 @@ public class Http2ConnectionHandlerTest { int newStreamId = 5; Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(newStreamId).setPriority(1) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.write(ctx, frame, promise); verify(local).createStream(eq(newStreamId), eq(1), eq(false)); verify(promise, never()).setFailure(any(Http2Exception.class)); @@ -470,7 +469,7 @@ public class Http2ConnectionHandlerTest { int newStreamId = 5; Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(newStreamId).setPriority(1) - .setEndOfStream(true).setHeaders(new Http2Headers.Builder().build()) + .setEndOfStream(true).setHeaders(Http2Headers.EMPTY_HEADERS) .build(); handler.write(ctx, frame, promise); verify(local).createStream(eq(newStreamId), eq(1), eq(true)); @@ -482,7 +481,7 @@ public class Http2ConnectionHandlerTest { public void outboundHeadersShouldOpenStreamForPush() throws Exception { Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(STREAM_ID).setPriority(1) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.write(ctx, frame, promise); verify(stream).openForPush(); verify(stream, never()).closeLocalSide(eq(ctx), eq(future)); @@ -494,7 +493,7 @@ public class Http2ConnectionHandlerTest { public void outboundHeadersShouldClosePushStream() throws Exception { Http2Frame frame = new DefaultHttp2HeadersFrame.Builder().setStreamId(STREAM_ID).setPriority(1) - .setEndOfStream(true).setHeaders(new Http2Headers.Builder().build()) + .setEndOfStream(true).setHeaders(Http2Headers.EMPTY_HEADERS) .build(); handler.write(ctx, frame, promise); verify(stream).openForPush(); @@ -509,7 +508,7 @@ public class Http2ConnectionHandlerTest { Http2Frame frame = new DefaultHttp2PushPromiseFrame.Builder().setStreamId(STREAM_ID) .setPromisedStreamId(PUSH_STREAM_ID) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.write(ctx, frame, promise); verify(promise).setFailure(any(Http2Exception.class)); verify(local, never()).reservePushStream(eq(PUSH_STREAM_ID), eq(stream)); @@ -521,7 +520,7 @@ public class Http2ConnectionHandlerTest { Http2Frame frame = new DefaultHttp2PushPromiseFrame.Builder().setStreamId(STREAM_ID) .setPromisedStreamId(PUSH_STREAM_ID) - .setHeaders(new Http2Headers.Builder().build()).build(); + .setHeaders(Http2Headers.EMPTY_HEADERS).build(); handler.write(ctx, frame, promise); verify(promise, never()).setFailure(any(Http2Exception.class)); verify(local).reservePushStream(eq(PUSH_STREAM_ID), eq(stream)); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/Http2FrameRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/DefaultHttp2FrameRoundtripTest.java similarity index 95% rename from codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/Http2FrameRoundtripTest.java rename to codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/DefaultHttp2FrameRoundtripTest.java index 434edb7d74..608108e73a 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/Http2FrameRoundtripTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/DefaultHttp2FrameRoundtripTest.java @@ -15,6 +15,7 @@ package io.netty.handler.codec.http2.draft10.frame; +import static io.netty.util.CharsetUtil.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -33,6 +34,7 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http2.draft10.DefaultHttp2Headers; import io.netty.handler.codec.http2.draft10.Http2Headers; import io.netty.util.NetUtil; import io.netty.util.ReferenceCountUtil; @@ -44,12 +46,10 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; -import com.google.common.base.Charsets; - /** * Tests encoding/decoding each HTTP2 frame type. */ -public class Http2FrameRoundtripTest { +public class DefaultHttp2FrameRoundtripTest { private static final EventLoopGroup group = new NioEventLoopGroup(); @@ -121,7 +121,7 @@ public class Http2FrameRoundtripTest { @Test public void headersFrameWithoutPriorityShouldMatch() throws Exception { Http2Headers headers = - new Http2Headers.Builder().setMethod("GET").setScheme("https") + new DefaultHttp2Headers.Builder().setMethod("GET").setScheme("https") .setAuthority("example.org").setPath("/some/path/resource2").build(); Http2HeadersFrame in = new DefaultHttp2HeadersFrame.Builder().setHeaders(headers).setEndOfStream(true) @@ -134,7 +134,7 @@ public class Http2FrameRoundtripTest { @Test public void headersFrameWithPriorityShouldMatch() throws Exception { Http2Headers headers = - new Http2Headers.Builder().setMethod("GET").setScheme("https") + new DefaultHttp2Headers.Builder().setMethod("GET").setScheme("https") .setAuthority("example.org").setPath("/some/path/resource2").build(); Http2HeadersFrame in = new DefaultHttp2HeadersFrame.Builder().setHeaders(headers).setEndOfStream(true) @@ -158,7 +158,7 @@ public class Http2FrameRoundtripTest { @Test public void pingFrameShouldMatch() throws Exception { - ByteBuf buf = Unpooled.copiedBuffer("01234567", Charsets.UTF_8); + ByteBuf buf = Unpooled.copiedBuffer("01234567", UTF_8); Http2PingFrame in = new DefaultHttp2PingFrame.Builder().setAck(true).setData(buf).build().retain(); @@ -180,7 +180,7 @@ public class Http2FrameRoundtripTest { @Test public void pushPromiseFrameShouldMatch() throws Exception { Http2Headers headers = - new Http2Headers.Builder().setMethod("GET").setScheme("https") + new DefaultHttp2Headers.Builder().setMethod("GET").setScheme("https") .setAuthority("example.org").setPath("/some/path/resource2").build(); Http2PushPromiseFrame in = new DefaultHttp2PushPromiseFrame.Builder().setHeaders(headers) @@ -224,7 +224,7 @@ public class Http2FrameRoundtripTest { @Test public void stressTest() throws Exception { Http2Headers headers = - new Http2Headers.Builder().setMethod("GET").setScheme("https") + new DefaultHttp2Headers.Builder().setMethod("GET").setScheme("https") .setAuthority("example.org").setPath("/some/path/resource2").build(); String text = "hello world"; int numStreams = 1000; diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/HeaderBlockRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/HeaderBlockRoundtripTest.java index 2e34a57ccc..f32ed6640d 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/HeaderBlockRoundtripTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/draft10/frame/HeaderBlockRoundtripTest.java @@ -19,6 +19,7 @@ package io.netty.handler.codec.http2.draft10.frame; import static org.junit.Assert.assertEquals; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http2.draft10.DefaultHttp2Headers; import io.netty.handler.codec.http2.draft10.Http2Exception; import io.netty.handler.codec.http2.draft10.Http2Headers; import io.netty.handler.codec.http2.draft10.frame.decoder.DefaultHttp2HeadersDecoder; @@ -52,33 +53,33 @@ public class HeaderBlockRoundtripTest { @Test public void roundtripShouldBeSuccessful() throws Http2Exception { Http2Headers in = - new Http2Headers.Builder().setMethod("GET").setScheme("https") + new DefaultHttp2Headers.Builder().setMethod("GET").setScheme("https") .setAuthority("example.org").setPath("/some/path/resource2") - .addHeader("accept", "image/png").addHeader("cache-control", "no-cache") - .addHeader("custom", "value1").addHeader("custom", "value2") - .addHeader("custom", "value3").addHeader("custom", "custom4").build(); + .add("accept", "image/png").add("cache-control", "no-cache") + .add("custom", "value1").add("custom", "value2") + .add("custom", "value3").add("custom", "custom4").build(); assertRoundtripSuccessful(in); } @Test public void successiveCallsShouldSucceed() throws Http2Exception { Http2Headers in = - new Http2Headers.Builder().setMethod("GET").setScheme("https") + new DefaultHttp2Headers.Builder().setMethod("GET").setScheme("https") .setAuthority("example.org").setPath("/some/path") - .addHeader("accept", "*/*").build(); + .add("accept", "*/*").build(); assertRoundtripSuccessful(in); in = - new Http2Headers.Builder().setMethod("GET").setScheme("https") + new DefaultHttp2Headers.Builder().setMethod("GET").setScheme("https") .setAuthority("example.org").setPath("/some/path/resource1") - .addHeader("accept", "image/jpeg").addHeader("cache-control", "no-cache") + .add("accept", "image/jpeg").add("cache-control", "no-cache") .build(); assertRoundtripSuccessful(in); in = - new Http2Headers.Builder().setMethod("GET").setScheme("https") + new DefaultHttp2Headers.Builder().setMethod("GET").setScheme("https") .setAuthority("example.org").setPath("/some/path/resource2") - .addHeader("accept", "image/png").addHeader("cache-control", "no-cache") + .add("accept", "image/png").add("cache-control", "no-cache") .build(); assertRoundtripSuccessful(in); }