package org.webpieces.httpfrontend2.api.mock2; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.xml.bind.DatatypeConverter; import org.webpieces.data.api.BufferCreationPool; import org.webpieces.data.api.BufferPool; import org.webpieces.data.api.DataWrapper; import org.webpieces.data.api.DataWrapperGenerator; import org.webpieces.data.api.DataWrapperGeneratorFactory; import org.webpieces.mock.MethodEnum; import org.webpieces.mock.MockSuperclass; import org.webpieces.mock.ParametersPassedIn; import org.webpieces.nio.api.channels.Channel; import org.webpieces.nio.api.channels.ChannelSession; import org.webpieces.nio.api.channels.TCPChannel; import org.webpieces.nio.api.handlers.DataListener; import org.webpieces.nio.impl.util.ChannelSessionImpl; import com.webpieces.hpack.api.HpackParser; import com.webpieces.hpack.api.HpackParserFactory; import com.webpieces.hpack.api.MarshalState; import com.webpieces.hpack.api.UnmarshalState; import com.webpieces.http2parser.api.Http2Parser; import com.webpieces.http2parser.api.Http2ParserFactory; import com.webpieces.http2parser.api.dto.SettingsFrame; import com.webpieces.http2parser.api.dto.lib.Http2Frame; import com.webpieces.http2parser.api.dto.lib.Http2Msg; public class MockHttp2Channel extends MockSuperclass implements TCPChannel { private static final DataWrapperGenerator dataGen = DataWrapperGeneratorFactory.createDataWrapperGenerator(); private enum Method implements MethodEnum { INCOMING_FRAME } private HpackParser parser; private Http2Parser frameParser; private UnmarshalState unmarshalState; private MarshalState marshalState; private DataListener listener; private boolean isClosed; private ChannelSession session = new ChannelSessionImpl(); public MockHttp2Channel() { BufferPool bufferPool = new BufferCreationPool(); parser = HpackParserFactory.createParser(bufferPool, false); unmarshalState = parser.prepareToUnmarshal(4096, 4096, 4096); marshalState = parser.prepareToMarshal(4096, 4096); frameParser = Http2ParserFactory.createParser(bufferPool); } @Override public CompletableFuture<Channel> connect(SocketAddress addr, DataListener listener) { throw new UnsupportedOperationException("not implemented but could easily be with a one liner"); } public void sendPreface() { String preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; byte[] bytes = preface.getBytes(StandardCharsets.UTF_8); ByteBuffer wrap = ByteBuffer.wrap(bytes); listener.incomingData(this, wrap); } public void sendPrefaceAndSettings(SettingsFrame settings) { String preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; byte[] bytes = preface.getBytes(StandardCharsets.UTF_8); DataWrapper data = parser.marshal(marshalState, settings); DataWrapper prefaceWrapper = dataGen.wrapByteArray(bytes); DataWrapper all = dataGen.chainDataWrappers(prefaceWrapper, data); ByteBuffer buf = ByteBuffer.wrap(all.createByteArray()); listener.incomingData(this, buf); } public void writeHexBack(String hex) { byte[] bytes = DatatypeConverter.parseHexBinary(hex.replaceAll("\\s+","")); ByteBuffer buf = ByteBuffer.wrap(bytes); listener.incomingData(this, buf); } public void write(Http2Msg msg) { if(listener == null) throw new IllegalStateException("Not connected so we cannot write back"); DataWrapper data = parser.marshal(marshalState, msg); byte[] bytes = data.createByteArray(); if(bytes.length == 0) throw new IllegalArgumentException("how do you marshal to 0 bytes...WTF"); ByteBuffer buf = ByteBuffer.wrap(bytes); listener.incomingData(this, buf); } public void writeFrame(Http2Frame frame) { DataWrapper data = frameParser.marshal(frame); byte[] bytes = data.createByteArray(); if(bytes.length == 0) throw new IllegalArgumentException("how do you marshal to 0 bytes...WTF"); ByteBuffer buf = ByteBuffer.wrap(bytes); listener.incomingData(this, buf); } @SuppressWarnings("unchecked") @Override public CompletableFuture<Channel> write(ByteBuffer b) { DataWrapper data = dataGen.wrapByteBuffer(b); parser.unmarshal(unmarshalState, data); List<Http2Msg> parsedFrames = unmarshalState.getParsedFrames(); return (CompletableFuture<Channel>) super.calledMethod(Method.INCOMING_FRAME, parsedFrames); } public Http2Msg getFrameAndClear() { List<Http2Msg> msgs = getFramesAndClear(); if(msgs.size() != 1) throw new IllegalStateException("not correct number of responses. number="+msgs.size()+" but expected 1. list="+msgs); return msgs.get(0); } @SuppressWarnings("unchecked") public List<Http2Msg> getFramesAndClear() { Stream<ParametersPassedIn> calledMethodList = super.getCalledMethods(Method.INCOMING_FRAME); Stream<Http2Msg> retVal = calledMethodList.map(p -> (List<Http2Msg>)p.getArgs()[0]) .flatMap(Collection::stream); return retVal.collect(Collectors.toList()); } public void setIncomingFrameDefaultReturnValue(CompletableFuture<Channel> future) { super.setDefaultReturnValue(Method.INCOMING_FRAME, future); } @Override public CompletableFuture<Channel> close() { isClosed = true; return null; } @Override public CompletableFuture<Channel> registerForReads() { // TODO Auto-generated method stub return null; } @Override public CompletableFuture<Channel> unregisterForReads() { // TODO Auto-generated method stub return null; } @Override public boolean isRegisteredForReads() { // TODO Auto-generated method stub return false; } @Override public InetSocketAddress getRemoteAddress() { // TODO Auto-generated method stub return null; } @Override public boolean isConnected() { // TODO Auto-generated method stub return false; } @Override public ChannelSession getSession() { return session ; } @Override public void setMaxBytesWriteBackupSize(int maxBytesBackup) { // TODO Auto-generated method stub } @Override public int getMaxBytesBackupSize() { // TODO Auto-generated method stub return 0; } @Override public boolean isSslChannel() { // TODO Auto-generated method stub return false; } @Override public void setReuseAddress(boolean b) { // TODO Auto-generated method stub } @Override public void setName(String string) { // TODO Auto-generated method stub } @Override public String getChannelId() { // TODO Auto-generated method stub return null; } @Override public String getName() { // TODO Auto-generated method stub return null; } @Override public void bind(SocketAddress addr) { // TODO Auto-generated method stub } @Override public boolean isBlocking() { // TODO Auto-generated method stub return false; } @Override public boolean isClosed() { return isClosed; } @Override public boolean isBound() { // TODO Auto-generated method stub return false; } @Override public InetSocketAddress getLocalAddress() { // TODO Auto-generated method stub return null; } @Override public boolean getKeepAlive() { // TODO Auto-generated method stub return false; } @Override public void setKeepAlive(boolean b) { // TODO Auto-generated method stub } public void assertNoIncomingMessages() { List<ParametersPassedIn> list = this.calledMethods.get(Method.INCOMING_FRAME); if(list == null) return; else if(list.size() != 0) throw new IllegalStateException("expected no method calls but method was called "+list.size()+" times. list="+list); } public void setDataListener(DataListener dataListener) { this.listener = dataListener; } public void simulateClose() { listener.farEndClosed(this); } }