changeset 1278:f48d4c62c2e3

Added redirection tests
author Jiri Vanek <jvanek@redhat.com>
date Wed, 06 Jan 2016 09:53:38 +0100
parents f589afe6e008
children 199702cfbca7
files ChangeLog tests/reproducers/simple/simpletest1/testcases/SimpleTest1CountRequests.java tests/reproducers/simple/simpletest1/testcases/SimpleTest1Test.java tests/reproducers/simple/simpletest1/testcases/SimpleTestDefaultRedirects.java tests/test-extensions/net/sourceforge/jnlp/ServerLauncher.java tests/test-extensions/net/sourceforge/jnlp/TinyHttpdImpl.java
diffstat 6 files changed, 658 insertions(+), 151 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Jan 05 10:27:13 2016 +0100
+++ b/ChangeLog	Wed Jan 06 09:53:38 2016 +0100
@@ -1,3 +1,16 @@
+2016-01-06  Jiri Vanek  <jvanek@redhat.com>
+
+	Added redirection tests
+	* tests/test-extensions/net/sourceforge/jnlp/ServerLauncher.java:  Enhanced so
+	it can redirect requests to another instance. Enhanced to be able to count requests
+	* tests/test-extensions/net/sourceforge/jnlp/TinyHttpdImpl.java: same
+	* tests/reproducers/simple/simpletest1/testcases/SimpleTest1Test.java: small
+	refactoring to reuse checking methods
+	* tests/reproducers/simple/simpletest1/testcases/SimpleTest1CountRequests.java:
+	added set of tests to test behavior under various redirect codes
+	* tests/reproducers/simple/simpletest1/testcases/SimpleTestDefaultRedirects.java:
+	Added FAILING tests for 2591 - counting ITW requests to test server
+
 2016-01-05  Jiri Vanek  <jvanek@redhat.com>
 
 	* NEWS: mentioned PR2779
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/simple/simpletest1/testcases/SimpleTest1CountRequests.java	Wed Jan 06 09:53:38 2016 +0100
@@ -0,0 +1,109 @@
+/* SimpleTest1Test.java
+ Copyright (C) 2011 Red Hat, Inc.
+
+ This file is part of IcedTea.
+
+ IcedTea is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 2.
+
+ IcedTea is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with IcedTea; see the file COPYING.  If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library.  Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module.  An independent module is a module which is not derived from
+ or based on this library.  If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.sourceforge.jnlp.ProcessResult;
+import net.sourceforge.jnlp.ServerAccess;
+import net.sourceforge.jnlp.ServerLauncher;
+import net.sourceforge.jnlp.annotations.Bug;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+
+import org.junit.Test;
+
+@Bug(id = "PR2591")
+public class SimpleTest1CountRequests {
+
+    private static final ServerAccess server = new ServerAccess();
+    private static ServerLauncher server0;
+    private static final Map<String, Map<String, Integer>> counter = new HashMap<>();
+    private static final List<String> hv = Arrays.asList(new String[]{ServerAccess.VERBOSE_OPTION, ServerAccess.HEADLES_OPTION});
+
+    @BeforeClass
+    public static void createCountingServer() {
+        server0 = ServerAccess.getIndependentInstance();
+        server0.setRequestsCounter(counter);
+    }
+
+    @AfterClass
+    public static void stopCountingServer() {
+        server0.stop();
+    }
+
+    @Bug(id = "PR2591")
+    @Test
+    public void testSimpletest1EachResourceOnePerRun() throws Exception {
+        server0.setSupportingHeadRequest(true);
+        counter.clear();
+        ProcessResult pr = ServerAccess.executeProcessUponURL(server.getJavawsLocation(),
+                hv,
+                server0.getUrl("/simpletest1.jnlp"),
+                null,
+                null
+        );
+        SimpleTest1Test.checkLaunched(pr);
+        Assert.assertTrue(counter.get("./simpletest1.jnlp").get("GET").equals(1)); //2 without bugfix
+        Assert.assertTrue(counter.get("./simpletest1.jnlp").get("HEAD").equals(1));
+        Assert.assertTrue(counter.get("./simpletest1.jar").get("GET").equals(1));//2 without bugfix
+        Assert.assertTrue(counter.get("./simpletest1.jar").get("HEAD").equals(1));
+
+    }
+
+    @Bug(id = "PR2591")
+    @Test
+    public void testSimpletest1EachResourceOnePerRunHeadsOff() throws Exception {
+        server0.setSupportingHeadRequest(false);
+        counter.clear();
+        ProcessResult pr = ServerAccess.executeProcessUponURL(server.getJavawsLocation(),
+                hv,
+                server0.getUrl("/simpletest1.jnlp"),
+                null,
+                null
+        );
+        SimpleTest1Test.checkLaunched(pr);
+        Assert.assertTrue(counter.get("./simpletest1.jnlp").get("GET").equals(2)); //3 without bugfix
+        Assert.assertTrue(counter.get("./simpletest1.jnlp").get("HEAD") == null);
+        Assert.assertTrue(counter.get("./simpletest1.jar").get("GET").equals(2));//3 without bugfix
+        Assert.assertTrue(counter.get("./simpletest1.jar").get("HEAD") == (null));
+
+    }
+
+}
--- a/tests/reproducers/simple/simpletest1/testcases/SimpleTest1Test.java	Tue Jan 05 10:27:13 2016 +0100
+++ b/tests/reproducers/simple/simpletest1/testcases/SimpleTest1Test.java	Wed Jan 06 09:53:38 2016 +0100
@@ -1,38 +1,38 @@
 /* SimpleTest1Test.java
-Copyright (C) 2011 Red Hat, Inc.
+ Copyright (C) 2011 Red Hat, Inc.
 
-This file is part of IcedTea.
+ This file is part of IcedTea.
 
-IcedTea is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License as published by
-the Free Software Foundation, version 2.
+ IcedTea is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 2.
 
-IcedTea is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
+ IcedTea is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
 
-You should have received a copy of the GNU General Public License
-along with IcedTea; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
+ You should have received a copy of the GNU General Public License
+ along with IcedTea; see the file COPYING.  If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
 
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library.  Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library.  Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
 
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module.  An independent module is a module which is not derived from
-or based on this library.  If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so.  If you do not wish to do so, delete this
-exception statement from your version.
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module.  An independent module is a module which is not derived from
+ or based on this library.  If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
  */
 
 import java.io.File;
@@ -50,14 +50,18 @@
 
 public class SimpleTest1Test {
 
-    private static ServerAccess server = new ServerAccess();
+    private static final ServerAccess server = new ServerAccess();
     private static final List<String> strict = Arrays.asList(new String[]{"-strict", ServerAccess.VERBOSE_OPTION});
 
-    private void checkLaunched(ProcessResult pr) {
+    static void checkLaunched(ProcessResult pr) {
         checkLaunched(pr, false);
     }
 
-    private void checkLaunched(ProcessResult pr, boolean negate) {
+    static void checkLaunched(ProcessResult pr, boolean negate) {
+        checkLaunched(pr, negate, true);
+    }
+
+    static void checkLaunched(ProcessResult pr, boolean negate, boolean checkTermination) {
         String s = "Good simple javaws exapmle";
         if (negate) {
             Assert.assertFalse("testSimpletest1lunchOk stdout should NOT contains " + s + " bud did", pr.stdout.contains(s));
@@ -71,8 +75,14 @@
             //disabled, unnecessary exceptions may occure
             //Assert.assertFalse("testSimpletest1lunchOk stderr should not contains " + ss + " but did", pr.stderr.contains(ss));
         }
-        Assert.assertFalse(pr.wasTerminated);
-        Assert.assertEquals((Integer) 0, pr.returnValue);
+        if (checkTermination) {
+            Assert.assertFalse(pr.wasTerminated);
+            if (negate) {
+                Assert.assertEquals((Integer) 1, pr.returnValue);
+            } else {
+                Assert.assertEquals((Integer) 0, pr.returnValue);
+            }
+       }
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/simple/simpletest1/testcases/SimpleTestDefaultRedirects.java	Wed Jan 06 09:53:38 2016 +0100
@@ -0,0 +1,290 @@
+/* SimpleTest1Test.java
+ Copyright (C) 2011 Red Hat, Inc.
+
+ This file is part of IcedTea.
+
+ IcedTea is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 2.
+
+ IcedTea is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with IcedTea; see the file COPYING.  If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library.  Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module.  An independent module is a module which is not derived from
+ or based on this library.  If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+
+import java.net.MalformedURLException;
+import java.util.Arrays;
+import java.util.List;
+import net.sourceforge.jnlp.OptionsDefinitions;
+import net.sourceforge.jnlp.ProcessResult;
+import net.sourceforge.jnlp.ServerAccess;
+import net.sourceforge.jnlp.ServerLauncher;
+import org.junit.Assert;
+
+import org.junit.Test;
+
+public class SimpleTestDefaultRedirects {
+
+    private static final ServerAccess server = new ServerAccess();
+
+    private static final String D = "-J-Dhttp.maxRedirects=20"; //default - https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html ,  but...
+    //unluckily, setting http.maxRedirects to eg 1 do not have testing benefit
+    //as httpconnection then jsut throws exception instead of returning header to app's investigations
+    //I doubt it is worthy to struggle with setInstanceFollowRedirects in production code
+    
+    private static final List<String> hr = Arrays.asList(new String[]{D, ServerAccess.HEADLES_OPTION, OptionsDefinitions.OPTIONS.REDIRECT.option});
+    private static final List<String> hrv = Arrays.asList(new String[]{D, ServerAccess.VERBOSE_OPTION, ServerAccess.HEADLES_OPTION, OptionsDefinitions.OPTIONS.REDIRECT.option});
+    private static final List<String> hv = Arrays.asList(new String[]{D, ServerAccess.VERBOSE_OPTION, ServerAccess.HEADLES_OPTION});
+
+/* creates redirecting instances so oe can debug itw against it */
+//    public static void main(String[] args) throws InterruptedException, MalformedURLException {
+//        ServerLauncher[] servers = new ServerLauncher[3];
+//
+//        ServerLauncher server0 = ServerAccess.getIndependentInstance();
+//        server0.setRedirect(ServerAccess.getInstance()); //redirecting to normal server singleton
+//        server0.setRedirectCode(301);
+//        servers[0] = server0;
+//
+//        ServerLauncher server1 = ServerAccess.getIndependentInstance();
+//        server1.setRedirect(server0);
+//        server1.setRedirectCode(301);
+//        servers[1] = server1;
+//
+//        ServerLauncher server2 = ServerAccess.getIndependentInstance();
+//        server2.setRedirect(server1);
+//        server2.setRedirectCode(301);
+//        servers[2] = server2;
+//
+//        System.out.println(server0);
+//        System.out.println(server1);
+//        System.out.println(server2);
+//
+//        try {
+//            System.out.println(server0.getUrl("/" + "simpletest1.jnlp"));
+//            System.out.println(server1.getUrl("/" + "simpletest1.jnlp"));
+//            System.out.println(server2.getUrl("/" + "simpletest1.jnlp"));
+//            while (true) {
+//                Thread.sleep(100);
+//            }
+//        } finally {
+//            for (ServerLauncher server : servers) {
+//                server.stop();
+//            }
+//        }
+//    }
+
+    public void testbody(List<String> args, boolean pass, int... redirectCodes) throws Exception {
+        testbody(args, pass, -1, redirectCodes);
+    }
+
+    public void testbody(List<String> args, boolean pass, int breakChain, int... redirectCodes) throws Exception {
+        if (redirectCodes.length < 1) {
+            throw new RuntimeException("At least one redrection server pelase");
+        }
+        ServerLauncher[] servers = new ServerLauncher[redirectCodes.length];
+
+        ServerLauncher server0 = ServerAccess.getIndependentInstance();
+        server0.setRedirect(ServerAccess.getInstance()); //redirecting to normal server singleton
+        server0.setRedirectCode(redirectCodes[0]); //redirecting by first code
+        servers[0] = server0;
+
+        //create redirect chain
+        for (int i = 1; i < redirectCodes.length; i++) {
+            ServerLauncher serverI = ServerAccess.getIndependentInstance();
+            serverI.setRedirect(servers[i - 1]); //redirecting to pevious in chain
+            serverI.setRedirectCode(redirectCodes[i]); //by  given code
+            servers[i] = serverI;
+
+        }
+        testbody(args, pass, breakChain, servers);
+    }
+
+    public void testbody(List<String> args, boolean pass, int breakChain, ServerLauncher[] servers) throws Exception {
+        if (breakChain >= 0) {
+            servers[breakChain].setRedirect(null);
+            servers[breakChain].stop();
+        }
+        ServerLauncher server3012378 = servers[servers.length - 1];
+        try {
+            //now connect to last in chain and we should always get reposnse from ServerAccess.getInstance()
+            ProcessResult pr = ServerAccess.executeProcessUponURL(server.getJavawsLocation(),
+                    args,
+                    server3012378.getUrl("/" + "simpletest1.jnlp"),
+                    null,
+                    null
+            );
+            SimpleTest1Test.checkLaunched(pr, !pass, false);
+            if (pass) {
+                Assert.assertTrue(0 == pr.returnValue);
+            } else {
+                //1.6 have wrong handling of pr.return value
+                //Assert.assertFalse(0 == pr.returnValue);
+            }
+        } finally {
+            for (int i = 0; i < servers.length; i++) {
+                if (i != breakChain) {
+                    ServerLauncher serverI = servers[i];
+                    try {
+                        serverI.setRedirect(null);
+                        serverI.stop();
+                    } catch (Exception ex) {
+                        ServerAccess.logException(ex);
+                    }
+                }
+            }
+        }
+    }
+
+    // note, tonly 308 needs help form ITW,others are redirected autmatically in http connection
+    // https://docs.oracle.com/javase/7/docs/api/java/net/HttpURLConnection.html#setInstanceFollowRedirects%28boolean%29
+    public void testbody308(boolean pass, int... redirectCodes) throws Exception {
+        if (pass) {
+            testbody(hr, pass, redirectCodes);
+        } else {
+            testbody(hv, pass, redirectCodes);
+        }
+    }
+
+    public void testbodyOthers(boolean pass, int... redirectCodes) throws Exception {
+        if (pass) {
+            testbody(hr, true, redirectCodes);
+        } else {
+            testbody(hv, true, redirectCodes);
+        }
+    }
+
+    //some chains tests
+    @Test
+    public void testSimpletest1RedirectChain1AllowedOk() throws Exception {
+        testbodyOthers(true, 301, 302, 303, 307);
+    }
+
+    @Test
+    public void testSimpletest1RedirectChain1NotAllowedOk() throws Exception {
+        testbodyOthers(false, 301, 302, 303, 307);
+    }
+
+    @Test
+    public void testSimpletest1RedirectChain2AllowedOk() throws Exception {
+        testbody308(true, 301, 308, 302, 303, 307);
+    }
+
+    @Test
+    public void testSimpletest1RedirectChain2NotAllowedOk() throws Exception {
+        server.executeJavawsClearCache();
+        testbody308(false, 301, 308, 302, 303, 307);
+    }
+
+    @Test
+    public void testSimpletest1RedirectChain3AllowedBroken() throws Exception {
+        server.executeJavawsClearCache();
+        testbody(hrv, false, 1, new int[]{301, 302, 302, 303, 307});
+    }
+
+    @Test
+    public void testSimpletest1RedirectChain3NotAllowedBroken() throws Exception {
+        server.executeJavawsClearCache();
+        testbody(hv, false, 1, new int[]{301, 302, 302, 303, 307});
+    }
+
+    @Test
+    public void testSimpletest1RedirectChain3AlowedCycle() throws Exception {
+        server.executeJavawsClearCache();
+        ServerLauncher[] servers = new ServerLauncher[3];
+
+        ServerLauncher server0 = ServerAccess.getIndependentInstance();
+        server0.setRedirect(ServerAccess.getInstance()); //redirecting to normal server singleton
+        server0.setRedirectCode(301);
+        servers[0] = server0;
+
+        ServerLauncher server1 = ServerAccess.getIndependentInstance();
+        server1.setRedirectCode(301);
+        servers[1] = server1;
+
+        ServerLauncher server2 = ServerAccess.getIndependentInstance();
+        server2.setRedirectCode(301);
+        servers[2] = server2;
+
+        server1.setRedirect(server2);
+        server2.setRedirect(server1);
+        testbody(hrv, false, -1, servers);
+    }
+
+    //end chains
+    // basic tests
+    @Test
+    public void testSimpletest1Redirect301AllowedOk() throws Exception {
+        testbodyOthers(true, 301);
+    }
+
+    @Test
+    public void testSimpletest1Redirect301NotAllowedOk() throws Exception {
+        testbodyOthers(false, 301);
+    }
+
+    @Test
+    public void testSimpletest1Redirect302AllowedOk() throws Exception {
+        testbodyOthers(true, 302);
+    }
+
+    @Test
+    public void testSimpletest1Redirect302NotAllowedOk() throws Exception {
+        testbodyOthers(false, 302);
+    }
+
+    @Test
+    public void testSimpletest1Redirect303AllowedOk() throws Exception {
+        testbodyOthers(true, 303);
+    }
+
+    @Test
+    public void testSimpletest1Redirect331NotAllowedOk() throws Exception {
+        testbodyOthers(false, 303);
+    }
+
+    @Test
+    public void testSimpletest1Redirect307AllowedOk() throws Exception {
+        testbodyOthers(true, 307);
+    }
+
+    @Test
+    public void testSimpletest1Redirect307NotAllowedOk() throws Exception {
+        testbodyOthers(false, 307);
+    }
+
+    @Test
+    public void testSimpletest1Redirect308AllowedOk() throws Exception {
+        testbody308(true, 308);
+    }
+
+    @Test
+    public void testSimpletest1Redirect308NotAllowedOk() throws Exception {
+        server.executeJavawsClearCache();
+        testbody308(false, 308);
+    }
+    // end basic tests
+
+}
--- a/tests/test-extensions/net/sourceforge/jnlp/ServerLauncher.java	Tue Jan 05 10:27:13 2016 +0100
+++ b/tests/test-extensions/net/sourceforge/jnlp/ServerLauncher.java	Wed Jan 06 09:53:38 2016 +0100
@@ -1,46 +1,46 @@
 /* ServerLauncher.java
-Copyright (C) 2011,2012 Red Hat, Inc.
+ Copyright (C) 2011,2012 Red Hat, Inc.
 
-This file is part of IcedTea.
-
-IcedTea is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License as published by
-the Free Software Foundation, version 2.
+ This file is part of IcedTea.
 
-IcedTea is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
+ IcedTea is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 2.
 
-You should have received a copy of the GNU General Public License
-along with IcedTea; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
+ IcedTea is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
 
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library.  Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
+ You should have received a copy of the GNU General Public License
+ along with IcedTea; see the file COPYING.  If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library.  Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
 
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module.  An independent module is a module which is not derived from
-or based on this library.  If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so.  If you do not wish to do so, delete this
-exception statement from your version.
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module.  An independent module is a module which is not derived from
+ or based on this library.  If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
  */
-
 package net.sourceforge.jnlp;
 
 import java.io.File;
 import java.net.MalformedURLException;
 import java.net.ServerSocket;
 import java.net.URL;
+import java.util.Map;
 
 /**
  * wrapper around tiny http server to separate lunch configurations and servers.
@@ -49,8 +49,8 @@
 public class ServerLauncher implements Runnable {
 
     /**
-     * default url name part.
-     * This can be changed in runtime, but will affect all following tasks upon those server
+     * default url name part. This can be changed in runtime, but will affect
+     * all following tasks upon those server
      */
     private String serverName = ServerAccess.DEFAULT_LOCALHOST_NAME;
     private boolean running;
@@ -108,12 +108,42 @@
         this(8181, new File(System.getProperty("user.dir")));
     }
 
+    /**
+     * When redirect is set, the requests to this server will just redirect to
+     * the underlying ServerLauncher
+     */
+    private ServerLauncher redirect = null;
+    /**
+     * one of: 301, 302,303, 307, 308,
+     */
+    private int redirectCode = 302;
+
+    public void setRedirect(ServerLauncher redirect) {
+        this.redirect = redirect;
+
+    }
+
+    public void setRedirectCode(int redirectPort) {
+        this.redirectCode = redirectPort;
+    }
+
+    //resoource -> request -> number of requests on of this rsource on this server
+    // eg   simpletest1.jnlp -> GET -> 3
+    private Map<String, Map<String, Integer>> requestsCounter;
+
+    public void setRequestsCounter(Map<String, Map<String, Integer>> requestsCounter) {
+        this.requestsCounter = requestsCounter;
+    }
+
     public void run() {
         running = true;
         try {
             serverSocket = new ServerSocket(port);
             while (running) {
                 TinyHttpdImpl server = new TinyHttpdImpl(serverSocket.accept(), dir, false);
+                server.setRedirect(redirect);
+                server.setRedirectCode(redirectCode);
+                server.setRequestsCounter(requestsCounter);
                 server.setSupportingHeadRequest(isSupportingHeadRequest());
                 server.start();
             }
--- a/tests/test-extensions/net/sourceforge/jnlp/TinyHttpdImpl.java	Tue Jan 05 10:27:13 2016 +0100
+++ b/tests/test-extensions/net/sourceforge/jnlp/TinyHttpdImpl.java	Wed Jan 06 09:53:38 2016 +0100
@@ -1,40 +1,39 @@
 /* TinyHttpdImpl.java
-Copyright (C) 2011,2012 Red Hat, Inc.
+ Copyright (C) 2011,2012 Red Hat, Inc.
 
-This file is part of IcedTea.
-
-IcedTea is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License as published by
-the Free Software Foundation, version 2.
+ This file is part of IcedTea.
 
-IcedTea is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
+ IcedTea is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 2.
 
-You should have received a copy of the GNU General Public License
-along with IcedTea; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
+ IcedTea is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
 
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library.  Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
+ You should have received a copy of the GNU General Public License
+ along with IcedTea; see the file COPYING.  If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library.  Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
 
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module.  An independent module is a module which is not derived from
-or based on this library.  If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so.  If you do not wish to do so, delete this
-exception statement from your version.
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module.  An independent module is a module which is not derived from
+ or based on this library.  If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
  */
-
 package net.sourceforge.jnlp;
 
 import java.io.BufferedReader;
@@ -47,19 +46,20 @@
 import java.net.Socket;
 import java.net.SocketException;
 import java.net.URLDecoder;
-import java.security.cert.CRL;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.StringTokenizer;
 
 import net.sourceforge.jnlp.cache.ResourceTracker;
 
 /**
- * based on http://www.mcwalter.org/technology/java/httpd/tiny/index.html
- * Very small implementation of http return headers for our served resources
- * Originally Licenced under GPLv2.0 
+ * based on http://www.mcwalter.org/technology/java/httpd/tiny/index.html Very
+ * small implementation of http return headers for our served resources
+ * Originally Licenced under GPLv2.0
  *
- * When resource starts with XslowX prefix, then resouce (without XslowX)
- * is returned, but its delivery is delayed
+ * When resource starts with XslowX prefix, then resouce (without XslowX) is
+ * returned, but its delivery is delayed
  */
 public class TinyHttpdImpl extends Thread {
 
@@ -143,52 +143,77 @@
                     String filePath = t.nextToken();
                     boolean slowSend = filePath.startsWith(XSX);
 
-                    if (slowSend) {
-                        filePath = filePath.replace(XSX, "/");
+                    if (requestsCounter != null) {
+                        String resource = filePath.replace(XSX, "/");
+                        resource = urlToFilePath(resource);
+                        Map<String, Integer> reosurceRecord = requestsCounter.get(resource);
+                        if (reosurceRecord == null) {
+                            reosurceRecord = new HashMap<>();
+                            requestsCounter.put(resource, reosurceRecord);
+                        }
+                        Integer i = reosurceRecord.get(request);
+                        if (i == null) {
+                            i = 0;
+                        }
+                        i++;
+                        reosurceRecord.put(request, i);
                     }
 
-                    ServerAccess.logOutputReprint("Getting- " + request + ": " + filePath);
-                    filePath = urlToFilePath(filePath);
+                    if (redirect != null) {
+                        String where = redirect.getUrl(filePath).toExternalForm();
+                        ServerAccess.logOutputReprint("Redirecting " + request + "as " + redirectCode + " to " + where);
+                        writer.writeBytes("HTTP/1.0 " + redirectCode + " Moved" + CRLF);
+                        writer.writeBytes("Location: " + where + CRLF);
+                        writer.writeBytes(CRLF);
+                    } else {
 
-                    File resource = new File(this.testDir, filePath);
+                        if (slowSend) {
+                            filePath = filePath.replace(XSX, "/");
+                        }
 
-                    if (!(resource.isFile() && resource.canRead())) {
-                        ServerAccess.logOutputReprint("Could not open file " + filePath);
-                        writer.writeBytes(HTTP_NOT_FOUND);
-                        continue;
-                    }
-                    ServerAccess.logOutputReprint("Serving- " + request + ": " + filePath);
+                        ServerAccess.logOutputReprint("Getting- " + request + ": " + filePath);
+                        filePath = urlToFilePath(filePath);
+
+                        File resource = new File(this.testDir, filePath);
 
-                    int resourceLength = (int) resource.length();
-                    byte[] buff = new byte[resourceLength];
-                    FileInputStream fis = new FileInputStream(resource);
-                    fis.read(buff);
-                    fis.close();
+                        if (!(resource.isFile() && resource.canRead())) {
+                            ServerAccess.logOutputReprint("Could not open file " + filePath);
+                            writer.writeBytes(HTTP_NOT_FOUND);
+                            continue;
+                        }
+                        ServerAccess.logOutputReprint("Serving- " + request + ": " + filePath);
 
-                    String contentType = "Content-Type: ";
-                    if (filePath.toLowerCase().endsWith(".jnlp")) {
-                        contentType += "application/x-java-jnlp-file";
-                    } else if (filePath.toLowerCase().endsWith(".jar")) {
-                        contentType += "application/x-jar";
-                    } else {
-                        contentType += "text/html";
-                    }
-                    String lastModified = "";
-                    if (supportLastModified) {
-                        lastModified = "Last-Modified: " + new Date(resource.lastModified()) + CRLF;
-                    }
-                    writer.writeBytes(HTTP_OK + "Content-Length:" + resourceLength + CRLF + lastModified + contentType + CRLF + CRLF);
+                        int resourceLength = (int) resource.length();
+                        byte[] buff = new byte[resourceLength];
+                        FileInputStream fis = new FileInputStream(resource);
+                        fis.read(buff);
+                        fis.close();
 
-                    if (isGetRequest) {
-                        if (slowSend) {
-                            byte[][] bb = splitArray(buff, 10);
-                            for (int j = 0; j < bb.length; j++) {
-                                Thread.sleep(2000);
-                                byte[] bs = bb[j];
-                                writer.write(bs, 0, bs.length);
+                        String contentType = "Content-Type: ";
+                        if (filePath.toLowerCase().endsWith(".jnlp")) {
+                            contentType += "application/x-java-jnlp-file";
+                        } else if (filePath.toLowerCase().endsWith(".jar")) {
+                            contentType += "application/x-jar";
+                        } else {
+                            contentType += "text/html";
+                        }
+                        String lastModified = "";
+                        if (supportLastModified) {
+                            lastModified = "Last-Modified: " + new Date(resource.lastModified()) + CRLF;
+                        }
+                        writer.writeBytes(HTTP_OK + "Content-Length:" + resourceLength + CRLF + lastModified + contentType + CRLF + CRLF);
+
+                        if (isGetRequest) {
+                            if (slowSend) {
+                                byte[][] bb = splitArray(buff, 10);
+                                for (int j = 0; j < bb.length; j++) {
+                                    Thread.sleep(2000);
+                                    byte[] bs = bb[j];
+                                    writer.write(bs, 0, bs.length);
+                                }
+                            } else {
+                                writer.write(buff, 0, resourceLength);
                             }
-                        } else {
-                            writer.write(buff, 0, resourceLength);
                         }
                     }
                 }
@@ -207,13 +232,14 @@
     }
 
     /**
-     * This function splits input array to severasl pieces
-     * from byte[length] splitt to n pieces s is retrunrd byte[n][length/n], except
-     * last piece which contains length%n
+     * This function splits input array to severasl pieces from byte[length]
+     * splitt to n pieces s is retrunrd byte[n][length/n], except last piece
+     * which contains length%n
      *
      * @param input - array to be splitted
      * @param pieces - to how many pieces it should be broken
-     * @return inidividual pices of original array, which concatet again givs original array
+     * @return inidividual pices of original array, which concatet again givs
+     * original array
      */
     public static byte[][] splitArray(byte[] input, int pieces) {
         int rest = input.length;
@@ -239,12 +265,13 @@
     }
 
     /**
-    * This function transforms a request URL into a path to a file which the server
-    * will return to the requester.
-    * @param url - the request URL
-    * @return a String representation of the local path to the file
-    * @throws UnsupportedEncodingException
-    */
+     * This function transforms a request URL into a path to a file which the
+     * server will return to the requester.
+     *
+     * @param url - the request URL
+     * @return a String representation of the local path to the file
+     * @throws UnsupportedEncodingException
+     */
     public static String urlToFilePath(String url) throws UnsupportedEncodingException {
         url = URLDecoder.decode(url, "UTF-8"); // Decode URL encoded charaters, eg "%3B" becomes ';'
         if (url.startsWith(XSX)) {
@@ -261,8 +288,9 @@
     }
 
     /**
-     * This function removes the HTTP Path Parameter from a given JAR URL, assuming that the
-     * path param delimiter is a semicolon
+     * This function removes the HTTP Path Parameter from a given JAR URL,
+     * assuming that the path param delimiter is a semicolon
+     *
      * @param url - the URL from which to remove the path parameter
      * @return the URL with the path parameter removed
      */
@@ -282,4 +310,31 @@
         }
         return url;
     }
+
+    /**
+     * When redirect is set, the requests to this server will just redirect to
+     * the underlying ServerLauncher
+     */
+    private ServerLauncher redirect = null;
+
+    void setRedirect(ServerLauncher redirect) {
+        this.redirect = redirect;
+    }
+
+    /**
+     * one of: 301, 302,303, 307, 308,
+     */
+    private int redirectCode = 302;
+
+    void setRedirectCode(int redirectPort) {
+        this.redirectCode = redirectPort;
+    }
+
+    //resoource -> request -> number of requests on of this rsource on this server
+    // eg   simpletest1.jnlp -> GET -> 3
+    private Map<String, Map<String, Integer>> requestsCounter;
+
+    public void setRequestsCounter(Map<String, Map<String, Integer>> requestsCounter) {
+        this.requestsCounter = requestsCounter;
+    }
 }