package org.webpieces.http2client.impl; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import org.webpieces.data.api.DataWrapper; import org.webpieces.data.api.DataWrapperGenerator; import org.webpieces.data.api.DataWrapperGeneratorFactory; import org.webpieces.nio.api.channels.Channel; import org.webpieces.nio.api.handlers.DataListener; 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.Http2ResponseListener; import com.webpieces.http2parser.api.dto.RstStreamFrame; import com.webpieces.http2parser.api.dto.lib.PartialStream; public class Layer1Incoming implements DataListener { private static final Logger log = LoggerFactory.getLogger(Layer1Incoming.class); private static final DataWrapperGenerator dataGen = DataWrapperGeneratorFactory.createDataWrapperGenerator(); private Http2ClientEngine layer2; private AtomicInteger nextAvailableStreamId = new AtomicInteger(1); public Layer1Incoming(Http2ClientEngine layer2) { this.layer2 = layer2; } public CompletableFuture<Void> sendInitialFrames() { return layer2.sendInitializationToSocket(); } public CompletableFuture<Void> sendPing() { return layer2.sendPing(); } public CompletableFuture<StreamWriter> sendRequest(Http2Headers request, Http2ResponseListener listener) { if(request.getStreamId() != 0) throw new IllegalStateException("Client MUST NOT set Http2Headers.streamId. that is filled in by library"); int streamId = getNextAvailableStreamId(); request.setStreamId(streamId); return layer2.sendFrameToSocket(request, listener) .thenApply(c -> createWriter(request, c)); } private StreamWriter createWriter(Http2Headers request, StreamWriter requestWriter) { StreamWriter writer = new Writer(request.getStreamId(), request.isEndOfStream(), requestWriter); return writer; } private class Writer implements StreamWriter { private StreamWriter requestWriter; private int streamId; private boolean isEndOfStream; public Writer(int streamId, boolean isEndOfStream, StreamWriter requestWriter) { this.streamId = streamId; this.isEndOfStream = isEndOfStream; this.requestWriter = requestWriter; } @Override public CompletableFuture<StreamWriter> send(PartialStream data) { if(isEndOfStream && !(data instanceof RstStreamFrame)) throw new IllegalStateException("Client has already sent a PartialStream" + " object with endOfStream=true so no more data can be sent"); if(data.isEndOfStream()) isEndOfStream = true; data.setStreamId(streamId); return requestWriter.send(data).thenApply(c -> this); } } private int getNextAvailableStreamId() { return nextAvailableStreamId.getAndAdd(2); } @Override public void incomingData(Channel channel, ByteBuffer b) { log.info(channel+"incoming data. size="+b.remaining()); DataWrapper data = dataGen.wrapByteBuffer(b); //log.info("data="+data.createStringFrom(0, data.getReadableSize(), StandardCharsets.UTF_8)); layer2.parse(data); } @Override public void farEndClosed(Channel channel) { layer2.farEndClosed(); } @Override public void failure(Channel channel, ByteBuffer data, Exception e) { log.warn("failure", e); } @Override public void applyBackPressure(Channel channel) { log.info("apply back pressure"); } @Override public void releaseBackPressure(Channel channel) { log.info("apply back pressure"); } }