Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
b3639a4
start
SentryMan Nov 6, 2025
b0c6729
Update pom.xml
SentryMan Nov 6, 2025
5808405
trust client
SentryMan Nov 6, 2025
8eb84d3
remove qlog
SentryMan Nov 7, 2025
b6ae47e
http1 https
SentryMan Nov 7, 2025
9331a2f
redirect to http3
SentryMan Nov 7, 2025
5165f5c
transport
SentryMan Nov 7, 2025
8b84adb
full transport
SentryMan Nov 7, 2025
4aafe10
fix test
SentryMan Nov 7, 2025
1a686a1
doc
SentryMan Nov 8, 2025
100b0bb
implement attributes
SentryMan Nov 8, 2025
5423cc7
Update FlupkeHttpServer.java
SentryMan Nov 8, 2025
0356eb4
tests
SentryMan Nov 8, 2025
01be3d2
rename
SentryMan Nov 8, 2025
ce41faf
Update FlupkeHttpServer.java
SentryMan Nov 8, 2025
e448780
rename ssl impl
SentryMan Nov 8, 2025
a6642ec
different ports
SentryMan Nov 8, 2025
f04ff28
Update WebTransportTest.java
SentryMan Nov 8, 2025
69186e0
flush on close
SentryMan Nov 8, 2025
673c19b
Update WebTransportTest.java
SentryMan Nov 8, 2025
6e50bfd
Update WebTransportTest.java
SentryMan Nov 8, 2025
bb31611
separate tests
SentryMan Nov 8, 2025
1ea3c8c
fix test
SentryMan Nov 8, 2025
f1a1f49
timeout?
SentryMan Nov 8, 2025
66b303c
Update WebTransportBaseTest.java
SentryMan Nov 8, 2025
e17fe3a
fix test (allegedly)
SentryMan Nov 9, 2025
2850674
don't disable certificate check
SentryMan Nov 9, 2025
60c0c05
fix jdk client http3 option
SentryMan Nov 9, 2025
006efd6
Update WebTransportTest.java
SentryMan Nov 9, 2025
0f581cf
is it timeout
SentryMan Nov 9, 2025
41a21a9
cert
SentryMan Nov 9, 2025
2786584
Update FlupkeHttpServer.java
SentryMan Nov 9, 2025
9974ef2
cert alias
SentryMan Nov 9, 2025
665062b
alias
SentryMan Nov 9, 2025
96241e9
logger
SentryMan Nov 10, 2025
00881c4
final
SentryMan Nov 10, 2025
da5057b
log protocol
SentryMan Nov 10, 2025
ed9e75a
Revert "log protocol"
SentryMan Nov 10, 2025
61455d6
more test
SentryMan Nov 10, 2025
a28e4ca
Reapply "log protocol"
SentryMan Nov 10, 2025
0d4dd92
Update FlupkeHttpServer.java
SentryMan Nov 10, 2025
699cdbd
Merge branch 'master' into http3
SentryMan Nov 10, 2025
0eea703
vt executor
SentryMan Nov 18, 2025
e7abfbd
Merge branch 'master' into http3
SentryMan Nov 18, 2025
cd2425c
Update module-info.java
SentryMan Nov 18, 2025
66ffb1c
final tests
SentryMan Nov 25, 2025
eb68758
new flupke version
SentryMan Nov 28, 2025
a7fe430
Merge branch 'master' into http3
SentryMan Nov 28, 2025
fa1c454
bump version
SentryMan Nov 28, 2025
9de7a61
Edit `FlupkeSystemLogger` method to lock (#329)
mechite Nov 28, 2025
8a0d22c
ensure the same port for http1
SentryMan Nov 28, 2025
f32abc0
ensure a different way
SentryMan Nov 28, 2025
f88b6ae
handle double close
SentryMan Nov 28, 2025
e463464
Update FlupkeExchange.java
SentryMan Nov 28, 2025
b3ccd9b
Merge branch 'master' into http3
SentryMan Dec 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions avaje-jex-http3-flupke/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.avaje</groupId>
<artifactId>avaje-jex-parent</artifactId>
<version>3.3</version>
</parent>
<artifactId>avaje-jex-http3-flupke</artifactId>
<version>0.1</version>
<properties>
<maven.compiler.release>21</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>tech.kwik</groupId>
<artifactId>flupke</artifactId>
<version>0.9</version>
</dependency>
<dependency>
<groupId>tech.kwik</groupId>
<artifactId>kwik</artifactId>
<version>0.10.7</version>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-jex-ssl</artifactId>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-jex-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.avaje.jex.http3.flupke;

import java.net.DatagramSocket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import io.avaje.jex.Jex;
import io.avaje.jex.http3.flupke.impl.H3ServerProvider;
import io.avaje.jex.http3.flupke.webtransport.WebTransportEntry;
import io.avaje.jex.http3.flupke.webtransport.WebTransportHandler;
import io.avaje.jex.spi.JexPlugin;
import tech.kwik.core.server.ServerConnectionConfig;
import tech.kwik.core.server.ServerConnector;
import tech.kwik.flupke.server.Http3ServerExtensionFactory;

public final class FlupkeJexPlugin implements JexPlugin {

private DatagramSocket socket;
private Map<String, Http3ServerExtensionFactory> extensions = Map.of();
private List<WebTransportEntry> wts = new ArrayList<>();
private Consumer<ServerConnector.Builder> consumer = b -> {};
private Consumer<ServerConnectionConfig.Builder> connection = b -> {};

private FlupkeJexPlugin() {}

public static FlupkeJexPlugin create() {
return new FlupkeJexPlugin();
}

public FlupkeJexPlugin customSocket(DatagramSocket socket) {
this.socket = socket;
return this;
}

public FlupkeJexPlugin extensions(Map<String, Http3ServerExtensionFactory> extensions) {
this.extensions = extensions;
return this;
}

public FlupkeJexPlugin connectorConfig(Consumer<ServerConnector.Builder> consumer) {
this.consumer = consumer;
return this;
}

public FlupkeJexPlugin connectionConfig(Consumer<ServerConnectionConfig.Builder> consumer) {
this.connection = consumer;
return this;
}

public FlupkeJexPlugin webTransport(String path, WebTransportHandler handler) {
this.wts.add(new WebTransportEntry(path, handler));
return this;
}

public FlupkeJexPlugin webTransport(String path, Consumer<WebTransportHandler.Builder> consumer) {
var b = WebTransportHandler.builder();
consumer.accept(b);
this.wts.add(new WebTransportEntry(path, b.build()));
return this;
}

@Override
public void apply(Jex jex) {
jex.config().serverProvider(new H3ServerProvider(consumer,connection, wts, extensions, socket));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package io.avaje.jex.http3.flupke.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpHeaders;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;

import tech.kwik.flupke.server.HttpServerRequest;
import tech.kwik.flupke.server.HttpServerResponse;

class FlupkeExchange extends HttpExchange {

private final HttpServerRequest request;
private final HttpServerResponse response;

private final Headers responseHeaders = new Headers();
private Headers requestHeaders;
private HttpContext ctx;
private int statusCode = 0;
private InputStream is;
private PlaceholderOutputStream os = new PlaceholderOutputStream();

public FlupkeExchange(HttpServerRequest request, HttpServerResponse response, HttpContext ctx) {
this.request = request;
this.response = response;
this.ctx = ctx;
this.is = request.body();
}

@Override
public Headers getRequestHeaders() {
if (requestHeaders == null) {
requestHeaders = new Headers(request.headers().map());
}
return requestHeaders;
}

@Override
public Headers getResponseHeaders() {
return responseHeaders;
}

@Override
public URI getRequestURI() {
return URI.create(request.path());
}

@Override
public String getRequestMethod() {
return request.method();
}

@Override
public HttpContext getHttpContext() {
return ctx;
}

@Override
public void close() {
try (var __ = is;
var ___ = response.getOutputStream(); ) {
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public InputStream getRequestBody() {
return is;
}

@Override
public OutputStream getResponseBody() {
return os;
}

@Override
public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
statusCode = rCode;
response.setHeaders(HttpHeaders.of(responseHeaders, (a, b) -> true));
response.setStatus(rCode);
os.wrapped = response.getOutputStream();
}

@Override
public InetSocketAddress getRemoteAddress() {
return null;
}

@Override
public int getResponseCode() {
return statusCode;
}

@Override
public InetSocketAddress getLocalAddress() {
return null;
}

@Override
public String getProtocol() {

return "h3";
}

@Override
public Object getAttribute(String name) {
throw new UnsupportedOperationException();
}

@Override
public void setAttribute(String name, Object value) {
throw new UnsupportedOperationException();
}

@Override
public void setStreams(InputStream i, OutputStream o) {
is = i;
os.wrapped = o;
}

@Override
public HttpPrincipal getPrincipal() {
throw new UnsupportedOperationException();
}

/**
* An OutputStream which wraps another wtStream which is supplied either at creation time, or
* sometime later. If a caller/user tries to write to this wtStream before the wrapped wtStream has
* been provided, then an IOException will be thrown.
*/
class PlaceholderOutputStream extends OutputStream {

OutputStream wrapped;

void setWrappedStream(OutputStream os) {
wrapped = os;
}

boolean isWrapped() {
return wrapped != null;
}

private void checkWrap() throws IOException {
if (wrapped == null) {
throw new IOException("response headers not sent yet");
}
}

@Override
public void write(int b) throws IOException {
checkWrap();
wrapped.write(b);
}

@Override
public void write(byte b[]) throws IOException {
checkWrap();
wrapped.write(b);
}

@Override
public void write(byte b[], int off, int len) throws IOException {
checkWrap();
wrapped.write(b, off, len);
}

@Override
public void flush() throws IOException {
checkWrap();
wrapped.flush();
}

@Override
public void close() throws IOException {
checkWrap();
wrapped.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.avaje.jex.http3.flupke.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

class FlupkeHttpContext extends HttpContext {

private final HttpSpiContextHandler jettyContextHandler;
private final HttpServer server;
private final Map<String, Object> attributes = new HashMap<>();
private final List<Filter> filters = new ArrayList<>();
private Authenticator authenticator;
private HttpHandler httpHandler;

protected FlupkeHttpContext(HttpServer server, HttpHandler handler) {
httpHandler = handler;
this.server = server;
jettyContextHandler = new HttpSpiContextHandler(this, handler);
}

protected HttpSpiContextHandler flupkeHandler() {
return jettyContextHandler;
}

@Override
public HttpHandler getHandler() {
return httpHandler;
}

@Override
public void setHandler(HttpHandler h) {
jettyContextHandler.setHttpHandler(h);
}

@Override
public String getPath() {
return "/";
}

@Override
public HttpServer getServer() {
return server;
}

@Override
public Map<String, Object> getAttributes() {
return attributes;
}

@Override
public List<Filter> getFilters() {
return filters;
}

@Override
public Authenticator setAuthenticator(Authenticator auth) {
Authenticator previous = authenticator;
authenticator = auth;
return previous;
}

@Override
public Authenticator getAuthenticator() {
return authenticator;
}
}
Loading
Loading