Skip to content

Commit

Permalink
First cut of frame encoding/decoding and session management for HTTP2
Browse files Browse the repository at this point in the history
Motivation:

Needed a rough performance comparison between SPDY and HTTP 2.0 framing.
Expected performance gains were seen in HTTP 2.0 due to header
compression.

Modifications:

Added a new codec-http2 module containing all of the new source and unit
tests.  Updated the top-level pom.xml to add this as a child module.

Result:

Netty will have basic support for HTTP2.
  • Loading branch information
nmittler committed Mar 27, 2014
1 parent 5d14124 commit 754e087
Show file tree
Hide file tree
Showing 81 changed files with 8,602 additions and 0 deletions.
61 changes: 61 additions & 0 deletions codec-http2/pom.xml
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId>
<version>5.0.0.Alpha2-SNAPSHOT</version>
</parent>

<artifactId>netty-codec-http2</artifactId>
<packaging>jar</packaging>

<name>Netty/Codec/HTTP2</name>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-codec-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-handler</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>hpack</artifactId>
<version>0.6.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0.1</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@@ -0,0 +1,45 @@
/*
* 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;

/**
* All error codes identified by the HTTP2 spec.
*/
public enum Http2Error {
NO_ERROR(0),
PROTOCOL_ERROR(1),
INTERNAL_ERROR(2),
FLOW_CONTROL_ERROR(3),
SETTINGS_TIMEOUT(4),
STREAM_CLOSED(5),
FRAME_SIZE_ERROR(6),
REFUSED_STREAM(7),
CANCEL(8),
COMPRESSION_ERROR(9),
CONNECT_ERROR(10),
ENHANCE_YOUR_CALM(11),
INADEQUATE_SECURITY(12);

private final int code;

private Http2Error(int code) {
this.code = code;
}

public int getCode() {
return this.code;
}
}
@@ -0,0 +1,50 @@
/*
* 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;

/**
* Exception thrown when an HTTP2 error was encountered.
*/
public class Http2Exception extends Exception {
private static final long serialVersionUID = -2292608019080068769L;

private final Http2Error error;

public Http2Exception(Http2Error error) {
this.error = error;
}

public Http2Exception(Http2Error error, String message) {
super(message);
this.error = error;
}

public Http2Error getError() {
return error;
}

public static Http2Exception format(Http2Error error, String fmt, Object... args) {
return new Http2Exception(error, String.format(fmt, args));
}

public static Http2Exception protocolError(String fmt, Object... args) {
return format(Http2Error.PROTOCOL_ERROR, fmt, args);
}

public static Http2Exception flowControlError(String fmt, Object... args) {
return format(Http2Error.FLOW_CONTROL_ERROR, fmt, args);
}
}
@@ -0,0 +1,193 @@
/*
* 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.Map.Entry;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMultimap;

public final class Http2Headers implements Iterable<Entry<String, String>> {

/**
* HTTP2 header names.
*/
public enum HttpName {
/**
* {@code :method}.
*/
METHOD(":method"),

/**
* {@code :scheme}.
*/
SCHEME(":scheme"),

/**
* {@code :authority}.
*/
AUTHORITY(":authority"),

/**
* {@code :path}.
*/
PATH(":path"),

/**
* {@code :status}.
*/
STATUS(":status");

private final String value;

private HttpName(String value) {
this.value = value;
}

public String value() {
return this.value;
}
}

private final ImmutableMultimap<String, String> headers;

private Http2Headers(Builder builder) {
this.headers = builder.map.build();
}

public String getHeader(String name) {
ImmutableCollection<String> col = getHeaders(name);
return col.isEmpty() ? null : col.iterator().next();
}

public ImmutableCollection<String> getHeaders(String name) {
return headers.get(name);
}

public String getMethod() {
return getHeader(HttpName.METHOD.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<Entry<String, String>> iterator() {
return headers.entries().iterator();
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((headers == null) ? 0 : headers.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Http2Headers other = (Http2Headers) obj;
if (headers == null) {
if (other.headers != null) {
return false;
}
} else if (!headers.equals(other.headers)) {
return false;
}
return true;
}

@Override
public String toString() {
return headers.toString();
}

public static class Builder {
private ImmutableMultimap.Builder<String, String> 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);
}
}
}
@@ -0,0 +1,36 @@
/*
* 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;

public class Http2StreamException extends Http2Exception {

private static final long serialVersionUID = -7658235659648480024L;
private final int streamId;

public Http2StreamException(int streamId, Http2Error error, String message) {
super(error, message);
this.streamId = streamId;
}

public Http2StreamException(int streamId, Http2Error error) {
super(error);
this.streamId = streamId;
}

public int getStreamId() {
return streamId;
}
}

0 comments on commit 754e087

Please sign in to comment.