@Override public Set<String> apply(Stream stream, Fields requestHeaders, Fields responseHeaders) { Set<String> result = Collections.<String>emptySet(); short version = stream.getSession().getVersion(); if (!isIfModifiedSinceHeaderPresent(requestHeaders) && isValidMethod(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).getValue()) && !isUserAgentBlacklisted(requestHeaders)) { String scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version)).getValue(); String host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version)).getValue(); String origin = scheme + "://" + host; String url = requestHeaders.get(HTTPSPDYHeader.URI.name(version)).getValue(); String absoluteURL = origin + url; if (LOG.isDebugEnabled()) LOG.debug("Applying push strategy for {}", absoluteURL); if (isMainResource(url, responseHeaders)) { MainResource mainResource = getOrCreateMainResource(absoluteURL); result = mainResource.getResources(); } else if (isPushResource(url, responseHeaders)) { Fields.Field referrerHeader = requestHeaders.get("referer"); if (referrerHeader != null) { String referrer = referrerHeader.getValue(); MainResource mainResource = mainResources.get(referrer); if (mainResource == null) mainResource = getOrCreateMainResource(referrer); Set<String> pushResources = mainResource.getResources(); if (!pushResources.contains(url)) mainResource.addResource(url, origin, referrer); else result = getPushResources(absoluteURL); } } if (LOG.isDebugEnabled()) LOG.debug("Pushing {} resources for {}: {}", result.size(), absoluteURL, result); } return result; }
@Test public void testSYNThenREPLYAndDATA() throws Exception { final byte[] data = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8); final String header = "foo"; InetSocketAddress proxyAddress = startProxy( startServer( new ServerSessionFrameListener.Adapter() { @Override public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) { Fields requestHeaders = synInfo.getHeaders(); Assert.assertNotNull(requestHeaders.get("via")); Assert.assertNotNull(requestHeaders.get(header)); Fields responseHeaders = new Fields(); responseHeaders.put(header, "baz"); stream.reply(new ReplyInfo(responseHeaders, false), new Callback.Adapter()); stream.data(new BytesDataInfo(data, true), new Callback.Adapter()); return null; } })); proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); Session client = factory.newSPDYClient(version).connect(proxyAddress, null); final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1); Fields headers = new Fields(); headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + proxyAddress.getPort()); headers.put(header, "bar"); client.syn( new SynInfo(headers, true), new StreamFrameListener.Adapter() { private final ByteArrayOutputStream result = new ByteArrayOutputStream(); @Override public void onReply(Stream stream, ReplyInfo replyInfo) { Fields headers = replyInfo.getHeaders(); Assert.assertNotNull(headers.get(header)); replyLatch.countDown(); } @Override public void onData(Stream stream, DataInfo dataInfo) { result.write(dataInfo.asBytes(true), 0, dataInfo.length()); if (dataInfo.isClose()) { Assert.assertArrayEquals(data, result.toByteArray()); dataLatch.countDown(); } } }); Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS)); client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS)); }
@Test public void testSYNThenReset() throws Exception { InetSocketAddress proxyAddress = startProxy( startServer( new ServerSessionFrameListener.Adapter() { @Override public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) { Assert.assertTrue(synInfo.isClose()); Fields requestHeaders = synInfo.getHeaders(); Assert.assertNotNull(requestHeaders.get("via")); stream .getSession() .rst( new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new Callback.Adapter()); return null; } })); proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); final CountDownLatch resetLatch = new CountDownLatch(1); Session client = factory .newSPDYClient(version) .connect( proxyAddress, new SessionFrameListener.Adapter() { @Override public void onRst(Session session, RstInfo rstInfo) { resetLatch.countDown(); } }); Fields headers = new Fields(); headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + proxyAddress.getPort()); client.syn(new SynInfo(headers, true), null); Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS)); client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS)); }
@Test public void testSYNThenSPDYNestedPushIsReceived() throws Exception { final byte[] data = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8); InetSocketAddress proxyAddress = startProxy( startServer( new ServerSessionFrameListener.Adapter() { @Override public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) { Fields responseHeaders = new Fields(); responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1"); responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK"); stream.reply(new ReplyInfo(responseHeaders, false), new Callback.Adapter()); final Fields pushHeaders = new Fields(); pushHeaders.put(HTTPSPDYHeader.URI.name(version), "/push"); stream.push( new PushInfo(5, TimeUnit.SECONDS, pushHeaders, false), new Promise.Adapter<Stream>() { @Override public void succeeded(Stream pushStream) { pushHeaders.put(HTTPSPDYHeader.URI.name(version), "/nestedpush"); pushStream.push( new PushInfo(5, TimeUnit.SECONDS, pushHeaders, false), new Adapter<Stream>() { @Override public void succeeded(Stream pushStream) { pushHeaders.put( HTTPSPDYHeader.URI.name(version), "/anothernestedpush"); pushStream.push( new PushInfo(5, TimeUnit.SECONDS, pushHeaders, false), new Adapter<Stream>() { @Override public void succeeded(Stream pushStream) { pushStream.data( new BytesDataInfo(data, true), new Callback.Adapter()); } }); pushStream.data( new BytesDataInfo(data, true), new Callback.Adapter()); } }); pushStream.data(new BytesDataInfo(data, true), new Callback.Adapter()); } }); stream.data(new BytesDataInfo(data, true), new Callback.Adapter()); return null; } })); proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); final CountDownLatch pushSynLatch = new CountDownLatch(3); final CountDownLatch pushDataLatch = new CountDownLatch(3); Session client = factory.newSPDYClient(version).connect(proxyAddress, null); Fields headers = new Fields(); headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + proxyAddress.getPort()); final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1); client.syn( new SynInfo(headers, true), new StreamFrameListener.Adapter() { // onPush for 1st push stream @Override public StreamFrameListener onPush(Stream stream, PushInfo pushInfo) { pushSynLatch.countDown(); return new StreamFrameListener.Adapter() { // onPush for 2nd nested push stream @Override public StreamFrameListener onPush(Stream stream, PushInfo pushInfo) { pushSynLatch.countDown(); return new Adapter() { // onPush for 3rd nested push stream @Override public StreamFrameListener onPush(Stream stream, PushInfo pushInfo) { pushSynLatch.countDown(); return new Adapter() { @Override public void onData(Stream stream, DataInfo dataInfo) { dataInfo.consume(dataInfo.length()); if (dataInfo.isClose()) pushDataLatch.countDown(); } }; } @Override public void onData(Stream stream, DataInfo dataInfo) { dataInfo.consume(dataInfo.length()); if (dataInfo.isClose()) pushDataLatch.countDown(); } }; } @Override public void onData(Stream stream, DataInfo dataInfo) { dataInfo.consume(dataInfo.length()); if (dataInfo.isClose()) pushDataLatch.countDown(); } }; } @Override public void onReply(Stream stream, ReplyInfo replyInfo) { replyLatch.countDown(); } @Override public void onData(Stream stream, DataInfo dataInfo) { dataInfo.consume(dataInfo.length()); if (dataInfo.isClose()) dataLatch.countDown(); } }); Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(pushSynLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS)); client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS)); }