package org.webpieces.http2client.integ; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import org.webpieces.http2client.api.Http2Socket; import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; import org.webpieces.util.threading.NamedThreadFactory; import com.webpieces.hpack.api.dto.Http2Headers; import com.webpieces.http2engine.api.client.Http2ResponseListener; import com.webpieces.http2engine.api.client.PushPromiseListener; import com.webpieces.http2parser.api.dto.lib.Http2Header; import com.webpieces.http2parser.api.dto.lib.Http2HeaderName; import com.webpieces.http2parser.api.dto.lib.PartialStream; public class IntegMultiThreaded { private static final Logger log = LoggerFactory.getLogger(IntegMultiThreaded.class); private static Executor executor = Executors.newFixedThreadPool(10, new NamedThreadFactory("deanClientThread")); private static SortedSet<Integer> sent = new TreeSet<>(); private static SortedSet<Integer> completed = new TreeSet<>(); private static SortedSet<Integer> completedPush = new TreeSet<>(); private static class WorkItem implements Runnable { private ChunkedResponseListener listener; private Http2Socket socket; private List<Http2Header> req; private int id; private int originalId; public WorkItem(Http2Socket socket, List<Http2Header> req2, int id, int originalId) { this.socket = socket; this.req = req2; this.id = id; this.originalId = originalId; listener = new ChunkedResponseListener(id); } public void run() { try { runImpl(); } catch(Throwable e) { log.warn("Exception", e); } } public void runImpl() { synchronized(sent) { sent.add(id); } Http2Headers request = new Http2Headers(req); request.setEndOfStream(true); socket.send(request, listener) .exceptionally(e -> { reportException(socket, e); return null; }); log.info("sent request. ID="+id+" streamId="+request.getStreamId()); int numRun = id - originalId; if(numRun >= -1) { log.info("exiting running more. numRun="+numRun+" id="+id+" orig="+originalId); return; } int newId = id+1; WorkItem work = new WorkItem(socket, req, newId, originalId); executor.execute(work); } }; public static void main(String[] args) throws InterruptedException { boolean isHttp = true; String path = "/"; //String host = www.google.com; //String host = "localhost"; //jetty String host = "nghttp2.org"; int port = 443; if(isHttp) port = 80; if(host.equals("localhost")) { path = "/test/data.txt"; //IF jetty, use a path with a bigger download port = 8443; if(isHttp) port = 8080; } List<Http2Header> req = createRequest(host, isHttp, path); log.info("starting socket"); InetSocketAddress addr = new InetSocketAddress(host, port); Http2Socket socket = IntegSingleRequest.createHttpClient("clientSocket", isHttp, addr); socket .connect(addr) .thenApply(s -> { for(int i = 0; i < 99; i+=100) { executor.execute(new WorkItem(socket, req, i, i)); } return s; }) .exceptionally(e -> { reportException(socket, e); return null; }); Thread.sleep(100000000); } private static Void reportException(Http2Socket socket, Throwable e) { log.error("exception on socket="+socket, e); return null; } private static class ChunkedResponseListener implements Http2ResponseListener, PushPromiseListener { private int id; public ChunkedResponseListener(int id) { this.id = id; } @Override public CompletableFuture<Void> incomingPartialResponse(PartialStream response) { log.info("incoming part of response="+response); if(response.isEndOfStream()) { synchronized (completed) { completed.add(id); } log.info("completed="+completed.size()+" completedPus="+completedPush.size()+" sent="+sent.size()+" list="+completed); } return CompletableFuture.completedFuture(null); } @Override public PushPromiseListener newIncomingPush(int streamId) { return this; } @Override public CompletableFuture<Void> incomingPushPromise(PartialStream response) { log.info("incoming push promise="+response); if(response.isEndOfStream()) { synchronized(this) { completedPush.add(id); log.info("completedPush="+completedPush+" sent="+sent.size()); } } return CompletableFuture.completedFuture(null); } } private static List<Http2Header> createRequest(String host, boolean isHttp, String path) { String scheme; if(isHttp) scheme = "http"; else scheme = "https"; List<Http2Header> headers = new ArrayList<>(); headers.add(new Http2Header(Http2HeaderName.METHOD, "GET")); headers.add(new Http2Header(Http2HeaderName.AUTHORITY, host)); headers.add(new Http2Header(Http2HeaderName.PATH, path)); headers.add(new Http2Header(Http2HeaderName.SCHEME, scheme)); headers.add(new Http2Header("host", host)); headers.add(new Http2Header(Http2HeaderName.ACCEPT, "*/*")); headers.add(new Http2Header(Http2HeaderName.ACCEPT_ENCODING, "gzip, deflate")); headers.add(new Http2Header(Http2HeaderName.USER_AGENT, "webpieces/1.15.0")); return headers; } }