2 Replies Latest reply on Sep 20, 2013 2:26 AM by guillaume berche

    Injecting fault in java.net.Socket

    guillaume berche Newbie

      Hello,

       

      I'm trying to use byteman as part of an integration test [1], to make sure a client configured to use an HTTP proxy for a REST API is indeed going through the HTTP proxy instead of making straight network calls. I'd like to invoke a custom helper within java.net.Socket but I understand the custom rules are expected to be loaded from the target classloader. What would you suggest to embedd this custom helper into the proper classloader? Is there any support in the BMUnit package to do so ? Any other suggestion on how to perform this integration test ?

       

      The unsuccesfull attempts I made are reproduced below

       

      When I have the following rule, I indeed see the IOException being thrown

       

      @RunWith(BMUnitRunner.class)

      @BMScript(value="trace", dir="target/test-classes")

      @BMRules(rules={

              @BMRule(name="throw IOException socket opening",

                      targetClass = "^java.net.Socket",

                      targetMethod = "<init> (String , int , InetAddress,  int)",

                      action = "throw new IOException()")})

      public class CloudFoundryClientTest {

       

      When I try to invoke a custom Helper, the helper seems silently not called.

       

      @RunWith(BMUnitRunner.class)

      @BMRules(rules={

              @BMRule(name="throw IOException socket opening",

                      targetClass = "^java.net.Socket",

                      targetMethod = "<init> (String , int , InetAddress,  int)",

                      helper = "org.cloudfoundry.client.lib.SocketDestHelper",

                      action = "alwaysThrowException")})

       

      public class SocketDestHelper {

       

          static ThreadLocal<Boolean> isSocketRestrictingOnlyLocalHost = new ThreadLocal<Boolean>();

       

          public static void setForbiddenOnCurrentThread() {

              isSocketRestrictingOnlyLocalHost.set(true);

          }

       

          public static void alwaysThrowException() throws IOException {

              throw new IOException("only proxy threads should go through external hosts");

          }

          public static void throwExceptionIfForbidden(String host, int port) throws IOException {

              if (isSocketRestrictingOnlyLocalHost.get()) {

                  if (host != "127.0.0.1") {

                      throw new IOException("only proxy threads should go through external hosts, got:host=" + host + " port=" + port);

                  }

              }

          }

      }

       

      Thanks in advance for your help,

       

      Guillaume.

       

      [1] Fixed regression: HTTP proxy config missing in OAuth by gberche-orange · Pull Request #69 · cloudfoundry/vcap-java-clie…

        • 1. Re: Injecting fault in java.net.Socket
          Andrew Dinn Master

          Hi Guillaume,

           

          It looks to me like the action in your BMRule annotation is invalid. I think it should be

           

          action="alwaysThrowException()"

           

          i.e.it needs to be a call to the method (he value of this field needs to be the text you would enter after the DO keyword if you were writing a Byteman rule).

           

          If you are not using any form of module loader (after following the link to your test it to me looks like this is the case) then you should ensure your helper class is in the system class path. With this type of setup the class loader of the target method will delegate to the system loader and find your helper class. So, with maven, you just need to ensure that your jar containing the helper class is declared as a test dependency.

           

          I recommend that you set system property org.jboss.byteman.verbose (to any value, e.g. true) when you run the test. It will force Byteman to print to System.out any errors which occur when it tries to load your rules. This may make it clear what is going wrong if your test fails.

           

          If you are using a module system such as JBoss Modules or OSGI then you should still put your helper class into the system path and then configure the module system to delegate loads for the package which contains the helper (this means yo probably need to put your helper in its own special package otherwise loads for your application packages will be delegated). There are other threads on this forum which explain how to configure module loader delegation for JBoss Modules so I won't repeat the details here. I know OSGI provides a similar option but I am no OSGI expert so you will need to check the OSGI documentation to find out how to do it.

           

          regards,

           

           

          Andrew Dinn

          • 2. Re: Injecting fault in java.net.Socket
            guillaume berche Newbie

            Thanks a lot Andrew for your response. Indeed this fixed it (along with making helper methods non static). I'm now properly injecting faults into java.net.Socket

             

            I tried extending this to the connect() method with the following rule:

             

            @BMRule(name="2c- throw IOException socket opening",
            targetClass = "^java.net.Socket",
            targetMethod = "connect(SocketAddress, int )",
            helper = "org.cloudfoundry.client.lib.SocketDestHelper",
            action = "alwaysThrowException()"),

             

             

            Unfortunately I run into the following issue:

             

            org.jboss.byteman.agent.Transformer : unexpected error injecting trigger for rule 2c- throw IOException socket opening into class sun.nio.ch.SocketAdaptor

            java.lang.NullPointerException

            java.lang.NullPointerException

                at org.jboss.byteman.agent.adapter.cfg.CFG.computeContainment(CFG.java:1108)

                at org.jboss.byteman.agent.adapter.cfg.CFG.carryForward(CFG.java:962)

                at org.jboss.byteman.agent.adapter.cfg.CFG.split(CFG.java:1222)

                at org.jboss.byteman.agent.adapter.RuleTriggerMethodAdapter.visitInsn(RuleTriggerMethodAdapter.java:644)

                at org.jboss.byteman.agent.adapter.EntryTriggerAdapter$EntryTriggerMethodAdapter.visitInsn(EntryTriggerAdapter.java:104)

                at org.jboss.byteman.objectweb.asm.tree.InsnNode.accept(Unknown Source)

                at org.jboss.byteman.objectweb.asm.tree.InsnList.accept(Unknown Source)

                at org.jboss.byteman.objectweb.asm.tree.MethodNode.accept(Unknown Source)

                at org.jboss.byteman.objectweb.asm.commons.JSRInlinerAdapter.visitEnd(Unknown Source)

                at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)

                at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)

                at org.jboss.byteman.agent.TransformContext.transform(TransformContext.java:150)

                at org.jboss.byteman.agent.Transformer.transform(Transformer.java:700)

                at org.jboss.byteman.agent.Transformer.tryTransform(Transformer.java:768)

                at org.jboss.byteman.agent.Transformer.transform(Transformer.java:322)

                at sun.instrument.TransformerManager.transform(TransformerManager.java:169)

                at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:365)

                at sun.nio.ch.SocketChannelImpl.socket(SocketChannelImpl.java:114)

                at sun.nio.ch.WindowsSelectorImpl.<init>(WindowsSelectorImpl.java:109)

                at sun.nio.ch.WindowsSelectorProvider.openSelector(WindowsSelectorProvider.java:26)

                at java.nio.channels.Selector.open(Selector.java:209)

             

            I wonder if I run into similar issue described into https://community.jboss.org/message/720426#720426 and [#BYTEMAN-216] NPE and unexpected error injecting trigger with synchronization and loops - JBoss Issue Tracker which appears to be open and hard to fix.

             

            Since I can't modify the instrumented code which is part of the JDK, I worked around the problem by instrumenting a caller method connect(SocketAddress) which has simple logic flow in it. I'd be interested to hear if there are other workarounds.

             

            Thanks in advance for your help,

             

            Guillaume.