package org.webpieces.http2client.impl; import java.net.InetSocketAddress; import java.util.concurrent.CompletableFuture; import org.webpieces.data.api.DataWrapper; import org.webpieces.http2client.api.Http2Socket; import org.webpieces.http2client.api.dto.Http2Request; import org.webpieces.http2client.api.dto.Http2Response; import org.webpieces.nio.api.channels.TCPChannel; import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; import com.webpieces.hpack.api.dto.Http2Headers; import com.webpieces.http2engine.api.StreamWriter; import com.webpieces.http2engine.api.client.Http2ClientEngine; import com.webpieces.http2engine.api.client.Http2ClientEngineFactory; import com.webpieces.http2engine.api.client.Http2ResponseListener; import com.webpieces.http2parser.api.dto.DataFrame; public class Http2SocketImpl implements Http2Socket { private static final Logger log = LoggerFactory.getLogger(Http2SocketImpl.class); private Layer1Incoming incoming; private Layer3Outgoing outgoing; public Http2SocketImpl(TCPChannel channel, Http2ClientEngineFactory factory) { outgoing = new Layer3Outgoing(channel, this); Http2ClientEngine parseLayer = factory.createClientParser(outgoing); incoming = new Layer1Incoming(parseLayer); } @Override public CompletableFuture<Http2Socket> connect(InetSocketAddress addr) { if(addr == null) throw new IllegalArgumentException("addr cannot be null"); return outgoing.connect(addr, incoming) .thenCompose(c -> incoming.sendInitialFrames()) //make sure 'sending' initial frames is part of connecting .thenApply(f -> { log.info("connecting complete as initial frames sent"); return this; }); } @Override public CompletableFuture<Http2Socket> close() { //TODO: For http/2, please send GOAWAY first(crap, do we need reason in the close method?...probably) return outgoing.close().thenApply(channel -> this); } @Override public CompletableFuture<Http2Response> send(Http2Request request) { SingleResponseListener responseListener = new SingleResponseListener(); if(request.getPayload() == null) { request.getHeaders().setEndOfStream(true); send(request.getHeaders(), responseListener); return responseListener.fetchResponseFuture(); } else if(request.getTrailingHeaders() == null) { request.getHeaders().setEndOfStream(false); DataFrame data = createData(request, true); return send(request.getHeaders(), responseListener) .thenCompose(writer -> writer.send(data)) .thenCompose(writer -> responseListener.fetchResponseFuture()); } request.getHeaders().setEndOfStream(false); DataFrame data = createData(request, false); request.getTrailingHeaders().setEndOfStream(true); return send(request.getHeaders(), responseListener) .thenCompose(writer -> writer.send(data)) .thenCompose(writer -> writer.send(request.getTrailingHeaders())) .thenCompose(writer -> responseListener.fetchResponseFuture()); } private DataFrame createData(Http2Request request, boolean isEndOfStream) { DataWrapper payload = request.getPayload(); DataFrame data = new DataFrame(); data.setEndOfStream(isEndOfStream); data.setData(payload); return data; } @Override public CompletableFuture<StreamWriter> send(Http2Headers request, Http2ResponseListener listener) { return incoming.sendRequest(request, listener); } @Override public CompletableFuture<Void> sendPing() { return incoming.sendPing(); } }