class Socks5IqHandler extends DefaultIQHandler {

    @Override
    protected List<Stanza> handleSet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext, SessionContext sessionContext, StanzaBroker stanzaBroker) {
        /*
            C: <iq from='requester@example.com/foo'
                    id='oqx6t1c9'
                    to='streamer.example.com'
                    type='set'>
                  <query xmlns='http://jabber.org/protocol/bytestreams'
                     sid='vxf9n471bn46'>
                    <activate>target@example.org/bar</activate>
                  </query>
               </iq>
               
            S: <iq from='streamer.example.com'
                    id='oqx6t1c9'
                    to='requester@example.com/foo'
                    type='result'/>
                    
                    
            SHA1 Hash of: (SID + Requester JID + Target JID)
         */
        
        try {
            XMLElement queryElm = stanza.getSingleInnerElementsNamed("query", NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS);
            XMLElement activateElm = queryElm.getSingleInnerElementsNamed("activate", NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS);
            
            String sid = queryElm.getAttributeValue("sid");
            
            Entity target = EntityImpl.parse(activateElm.getInnerText().getText());
            
            Entity requester = stanza.getFrom();
            
            String hash = DigestUtils.shaHex(sid + requester.getFullQualifiedName() + target.getFullQualifiedName());
            
            if(connections.activate(hash)) {
                Stanza result = StanzaBuilder.createIQStanza(jid, requester, IQStanzaType.RESULT, stanza.getID()).build();
                return Collections.singletonList(result);
            } else {
                throw new RuntimeException("Pair not found");
            }
        } catch(Exception e) {
            return Collections.singletonList(ServerErrorResponses.getStanzaError(StanzaErrorCondition.BAD_REQUEST, stanza, StanzaErrorType.CANCEL, null, null, null));
        }
    }

}

class Socks5IqHandlerTest extends DefaultIQHandler {

    private static final Entity FROM;
    private static final Entity TARGET;
    private static final Entity TO;
    private ServerRuntimeContext serverRuntimeContext;
    private SessionContext sessionContext;
    private IQStanza stanza;
    private Socks5ConnectionsRegistry connectionsRegistry;
    private Entity jid;
    private InetSocketAddress proxyAddress;
    private Socks5IqHandler handler;

    @Test
    public void handleSetActivate() throws BindException, XMLSemanticError {

        IQStanza request = (IQStanza) IQStanza.getWrapper(StanzaBuilder
                .createIQStanza(FROM, TO, IQStanzaType.SET, "id1")
                .startInnerElement("query", NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS).addAttribute("sid", "sid1")
                .startInnerElement("activate", NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS)
                .addText(TARGET.getFullQualifiedName()).build());

        String hash = DigestUtils.shaHex("sid1" + FROM.getFullQualifiedName() + TARGET.getFullQualifiedName());
        when(connectionsRegistry.activate(hash)).thenReturn(true);

        List<Stanza> responses = handler.handleSet(request, serverRuntimeContext, sessionContext, null);
        Stanza response = responses.get(0);

        Stanza expected = StanzaBuilder.createIQStanza(TO, FROM, IQStanzaType.RESULT, "id1").build();

        StanzaAssert.assertEquals(expected, response);

        verify(connectionsRegistry).activate(hash);
    }

}