Skip to content

Commit af3fada

Browse files
committed
Merge branch 'http2'
1 parent 5a10a75 commit af3fada

39 files changed

+3295
-64
lines changed

build.gradle

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tasks.withType(Test) {
2525
jvmArgs += "--add-opens=jdk.httpserver/com.sun.net.httpserver=ALL-UNNAMED"
2626
systemProperty("java.util.logging.config.file","logging.properties")
2727
systemProperty("com.sun.net.httpserver.HttpServerProvider","robaho.net.httpserver.DefaultHttpServerProvider")
28+
systemProperty("robaho.net.httpserver.http2MaxConcurrentStreams","5000")
2829
// systemProperty("javax.net.debug","ssl:handshake:verbose:keymanager:trustmanager")
2930
}
3031

@@ -128,8 +129,11 @@ task runSimpleFileServer(type: Test) {
128129
classpath sourceSets.test.runtimeClasspath
129130
main "SimpleFileServer"
130131
systemProperties = props
131-
args = ['fileserver','8888','fileserver/logfile.txt']
132-
// debug true
132+
args = ['fileserver','8080','fileserver/logfile.txt']
133+
debugOptions {
134+
enabled = true
135+
suspend = false
136+
}
133137
}
134138
}
135139
}

gradlew

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

install_local.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mvn install:install-file -Dfile=build/libs/httpserver.jar -DgroupId=io.github.robaho -DartifactId=httpserver -Dversion=$1 -Dpackaging=jar -DgeneratePom=true

logging.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ handlers = java.util.logging.ConsoleHandler
33
java.util.logging.ConsoleHandler.level = ALL
44
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
55
java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] [%2$s] %5$s %6$s %n
6-
robaho.net.level=FINEST
6+
robaho.net.level=INFO

src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
exports robaho.net.httpserver;
33
exports robaho.net.httpserver.extras;
44
exports robaho.net.httpserver.websockets;
5+
exports robaho.net.httpserver.http2;
56

67
requires transitive java.logging;
78
requires transitive java.net.http;
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package robaho.net.httpserver;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.io.OutputStream;
6+
import java.net.InetSocketAddress;
7+
import java.net.URI;
8+
9+
import com.sun.net.httpserver.Headers;
10+
import com.sun.net.httpserver.HttpContext;
11+
import com.sun.net.httpserver.HttpExchange;
12+
import com.sun.net.httpserver.HttpPrincipal;
13+
14+
import robaho.net.httpserver.http2.HTTP2Stream;
15+
16+
public class Http2ExchangeImpl extends HttpExchange {
17+
private final Headers request;
18+
private final Headers response;
19+
private final InputStream in;
20+
private final OutputStream out;
21+
private final URI uri;
22+
private final String method;
23+
private final HttpContext ctx;
24+
private final HTTP2Stream stream;
25+
private HttpPrincipal principal;
26+
private int responseCode;
27+
28+
public Http2ExchangeImpl(HTTP2Stream stream, URI uri, String method, HttpContext ctx, Headers request, Headers response, InputStream in, OutputStream out) {
29+
this.request = request;
30+
this.response = response;
31+
this.stream = stream;
32+
this.in = in;
33+
this.out = out;
34+
this.uri = uri;
35+
this.method = method;
36+
this.ctx = ctx;
37+
}
38+
39+
@Override
40+
public Headers getRequestHeaders() {
41+
return request;
42+
}
43+
44+
@Override
45+
public Headers getResponseHeaders() {
46+
return response;
47+
}
48+
49+
@Override
50+
public InputStream getRequestBody() {
51+
return in;
52+
}
53+
54+
@Override
55+
public OutputStream getResponseBody() {
56+
return out;
57+
}
58+
59+
@Override
60+
public URI getRequestURI() {
61+
return uri;
62+
}
63+
64+
@Override
65+
public String getRequestMethod() {
66+
return method;
67+
}
68+
69+
@Override
70+
public HttpContext getHttpContext() {
71+
return ctx;
72+
}
73+
74+
@Override
75+
public void close() {
76+
stream.close();
77+
}
78+
79+
@Override
80+
public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
81+
if(responseLength>0) {
82+
response.set("Content-Length", Long.toString(responseLength));
83+
} else if(responseLength==0) {
84+
// no chunked encoding so just ignore
85+
} else {
86+
// -1 means no data will be sent, so should set end of stream
87+
// response.set("Content-Length", Long.toString(responseLength));
88+
}
89+
response.set(":status",Long.toString(rCode));
90+
responseCode = rCode;
91+
stream.writeResponseHeaders();
92+
}
93+
94+
@Override
95+
public InetSocketAddress getRemoteAddress() {
96+
return stream.getRemoteAddress();
97+
}
98+
99+
@Override
100+
public InetSocketAddress getLocalAddress() {
101+
return stream.getLocalAddress();
102+
}
103+
104+
@Override
105+
public int getResponseCode() {
106+
return responseCode;
107+
}
108+
109+
@Override
110+
public String getProtocol() {
111+
return "HTTP/2";
112+
}
113+
114+
@Override
115+
public Object getAttribute(String name) {
116+
return ctx.getAttributes().get(name);
117+
}
118+
119+
@Override
120+
public void setAttribute(String name, Object value) {
121+
ctx.getAttributes().put(name, value);
122+
}
123+
124+
@Override
125+
public void setStreams(InputStream i, OutputStream o) {
126+
throw new UnsupportedOperationException("Not implemented yet");
127+
}
128+
129+
@Override
130+
public HttpPrincipal getPrincipal() {
131+
return principal;
132+
}
133+
}

src/main/java/robaho/net/httpserver/HttpConnection.java

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import java.io.OutputStream;
3333
import java.lang.System.Logger;
3434
import java.lang.System.Logger.Level;
35+
import java.net.InetSocketAddress;
3536
import java.net.Socket;
37+
import java.util.concurrent.atomic.AtomicLong;
3638

3739
import javax.net.ssl.SSLSession;
3840
import javax.net.ssl.SSLSocket;
@@ -42,7 +44,7 @@
4244
* one of these is hung from the selector attachment and is used to locate
4345
* everything from that.
4446
*/
45-
class HttpConnection {
47+
public class HttpConnection {
4648
private static final Logger logger = System.getLogger("robaho.net.httpserver");
4749

4850
HttpContextImpl context;
@@ -57,27 +59,40 @@ class HttpConnection {
5759
volatile long lastActivityTime;
5860
volatile boolean noActivity;
5961
volatile boolean inRequest;
60-
volatile long requestCount;
62+
63+
public AtomicLong requestCount = new AtomicLong();
64+
private final String connectionId;
65+
66+
public boolean isClosed() {
67+
return closed;
68+
}
69+
70+
HttpConnection(Socket socket) throws IOException {
71+
this.socket = socket;
72+
this.is = new NoSyncBufferedInputStream(new ActivityTimerInputStream(socket.getInputStream()));
73+
this.os = new NoSyncBufferedOutputStream(new ActivityTimerOutputStream(socket.getOutputStream()));
74+
connectionId = "["+socket.getLocalPort()+"."+socket.getPort()+"]";
75+
}
6176

6277
SSLSession getSSLSession() {
6378
return (socket instanceof SSLSocket ssl) ? ssl.getHandshakeSession() : null;
6479
}
6580

81+
public boolean isSSL() {
82+
return socket instanceof SSLSocket;
83+
}
84+
6685
@Override
6786
public String toString() {
68-
final var sb = new StringBuilder(HttpConnection.class.getSimpleName());
69-
if (socket != null) {
70-
sb.append(" (");
71-
sb.append(socket);
72-
sb.append(")");
73-
}
74-
return sb.toString();
87+
return connectionId;
7588
}
7689

77-
HttpConnection(Socket socket) throws IOException {
78-
this.socket = socket;
79-
this.is = new NoSyncBufferedInputStream(new ActivityTimerInputStream(socket.getInputStream()));
80-
this.os = new NoSyncBufferedOutputStream(new ActivityTimerOutputStream(socket.getOutputStream()));
90+
public InetSocketAddress getRemoteAddress() {
91+
return (InetSocketAddress) socket.getRemoteSocketAddress();
92+
}
93+
94+
public InetSocketAddress getLocalAddress() {
95+
return (InetSocketAddress) socket.getLocalSocketAddress();
8196
}
8297

8398
void setContext(HttpContextImpl ctx) {
@@ -95,7 +110,7 @@ synchronized void close() {
95110
closed = true;
96111

97112
if (socket != null) {
98-
if(requestCount==0) {
113+
if(requestCount.get()==0) {
99114
logger.log(Level.WARNING, "closing connection: remote "+socket.getRemoteSocketAddress() + " with 0 requests");
100115
} else {
101116
logger.log(Level.TRACE, () -> "Closing connection: remote " + socket.getRemoteSocketAddress());

src/main/java/robaho/net/httpserver/ServerConfig.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
*/
3434

3535
@SuppressWarnings("removal")
36-
class ServerConfig {
36+
public class ServerConfig {
3737

3838
private static final int DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS = 10000; // 10 sec.
3939

@@ -50,6 +50,10 @@ class ServerConfig {
5050
private static final int DEFAULT_MAX_REQ_HEADERS = 200;
5151
private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024;
5252

53+
private static final int DEFAULT_HTTP2_MAX_FRAME_SIZE = 16384;
54+
private static final int DEFAULT_HTTP2_INITIAL_WINDOW_SIZE = 65535;
55+
private static final int DEFAULT_HTTP2_MAX_CONCURRENT_STREAMS = -1; // use -1 for no limit
56+
5357
private static long idleTimerScheduleMillis;
5458
private static long idleIntervalMillis;
5559
// The maximum number of bytes to drain from an inputstream
@@ -71,6 +75,12 @@ class ServerConfig {
7175
// the value of the TCP_NODELAY socket-level option
7276
private static boolean noDelay;
7377

78+
private static boolean http2OverSSL;
79+
private static boolean http2OverNonSSL;
80+
private static int http2MaxFrameSize;
81+
private static int http2InitialWindowSize;
82+
private static int http2MaxConcurrentStreams;
83+
7484
static {
7585
java.security.AccessController.doPrivileged(
7686
new PrivilegedAction<Void>() {
@@ -126,6 +136,14 @@ public Void run() {
126136

127137
noDelay = Boolean.getBoolean(pkg + ".nodelay");
128138

139+
http2OverSSL = Boolean.getBoolean(pkg + ".http2OverSSL");
140+
http2OverNonSSL = Boolean.getBoolean(pkg + ".http2OverNonSSL");
141+
142+
http2MaxFrameSize = Integer.getInteger(pkg + ".http2MaxFrameSize", DEFAULT_HTTP2_MAX_FRAME_SIZE);
143+
http2InitialWindowSize = Integer.getInteger(pkg + ".http2InitialWindowSize", DEFAULT_HTTP2_INITIAL_WINDOW_SIZE);
144+
145+
http2MaxConcurrentStreams = Integer.getInteger(pkg + ".http2MaxConcurrentStreams", DEFAULT_HTTP2_MAX_CONCURRENT_STREAMS);
146+
129147
return null;
130148
}
131149
});
@@ -216,4 +234,23 @@ static long getReqRspTimerScheduleMillis() {
216234
static boolean noDelay() {
217235
return noDelay;
218236
}
237+
238+
public static boolean http2OverSSL() {
239+
return http2OverSSL;
240+
}
241+
public static boolean http2OverNonSSL() {
242+
return http2OverNonSSL;
243+
}
244+
public static int http2MaxFrameSize() {
245+
return http2MaxFrameSize;
246+
}
247+
public static int http2InitialWindowSize() {
248+
return http2InitialWindowSize;
249+
}
250+
/**
251+
* @return the maximum number of concurrent streams per connection, or -1 for no limit
252+
*/
253+
public static int http2MaxConcurrentStreams() {
254+
return http2MaxConcurrentStreams;
255+
}
219256
}

0 commit comments

Comments
 (0)