All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.apigee.trireme.node12.modules.TCPWrap Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2013 Apigee Corporation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package io.apigee.trireme.node12.modules;

import io.apigee.trireme.core.NodeRuntime;
import io.apigee.trireme.core.InternalNodeModule;
import io.apigee.trireme.core.Utils;
import io.apigee.trireme.core.internal.AbstractIdObject;
import io.apigee.trireme.core.internal.IdPropertyMap;
import io.apigee.trireme.core.internal.ScriptRunner;
import io.apigee.trireme.kernel.OSException;
import io.apigee.trireme.kernel.handles.AbstractHandle;
import io.apigee.trireme.kernel.handles.IOCompletionHandler;
import io.apigee.trireme.kernel.handles.NIOSocketHandle;
import io.apigee.trireme.kernel.handles.SocketHandle;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.apigee.trireme.core.ArgUtils.*;

import java.lang.reflect.InvocationTargetException;
import java.net.Inet6Address;
import java.net.InetSocketAddress;

/**
 * Node's own script modules use this internal module to implement the guts of async TCP.
 */
public class TCPWrap
    implements InternalNodeModule
{
    protected static final Logger log = LoggerFactory.getLogger(TCPWrap.class);

    @Override
    public String getModuleName()
    {
        return "tcp_wrap";
    }

    @Override
    public Scriptable registerExports(Context cx, Scriptable scope, NodeRuntime runner)
        throws InvocationTargetException, IllegalAccessException, InstantiationException
    {
        ScriptableObject exports = (ScriptableObject)cx.newObject(scope);

        Function tcpImpl = new TCPImpl().exportAsClass(exports);
        exports.put(TCPImpl.CLASS_NAME, exports, tcpImpl);
        Function connectImpl = new ConnectImpl().exportAsClass(exports);
        exports.put(ConnectImpl.CLASS_NAME, exports, connectImpl);
        return exports;
    }

    public static class TCPImpl
        extends JavaStreamWrap.StreamWrapImpl
    {
        public static final String CLASS_NAME = "TCP";

        private static final IdPropertyMap props = new IdPropertyMap(CLASS_NAME);

        private Function     onConnection;
        private SocketHandle sockHandle;

        private static final int
            Id_onconnection = MAX_PROPERTY + 1,
            Id_bind = MAX_METHOD + 1,
            Id_bind6 = MAX_METHOD + 2,
            Id_listen = MAX_METHOD + 3,
            Id_connect = MAX_METHOD + 4,
            Id_connect6 = MAX_METHOD + 5,
            Id_shutdown = MAX_METHOD + 6,
            Id_getsockname = MAX_METHOD + 7,
            Id_getpeername = MAX_METHOD + 8,
            Id_setnodelay = MAX_METHOD + 9,
            Id_setkeepalive = MAX_METHOD + 10;

        static {
            JavaStreamWrap.StreamWrapImpl.defineIds(props);
            props.addProperty("onconnection", Id_onconnection, 0);
            props.addMethod("bind", Id_bind, 2);
            props.addMethod("bind6", Id_bind6, 2);
            props.addMethod("listen", Id_listen, 1);
            props.addMethod("connect", Id_connect, 3);
            props.addMethod("connect6", Id_connect6, 3);
            props.addMethod("shutdown", Id_shutdown, 1);
            props.addMethod("getsockname", Id_getsockname, 1);
            props.addMethod("getpeername", Id_getpeername, 1);
            props.addMethod("setNoDelay", Id_setnodelay, 1);
            props.addMethod("setKeepAlive", Id_setkeepalive, 1);
        }

        public TCPImpl()
        {
            super(props);
        }

        protected TCPImpl(SocketHandle handle, ScriptRunner runtime)
        {
            super(handle, runtime, props);
            this.sockHandle = handle;
            this.pinnable = true;
        }

        public void setSocketHandle(SocketHandle handle)
        {
            super.setHandle(handle);
            this.sockHandle = handle;
        }

        @Override
        protected JavaStreamWrap.StreamWrapImpl defaultConstructor(Context cx, Object[] args)
        {
            ScriptRunner runner = (ScriptRunner)cx.getThreadLocal(ScriptRunner.RUNNER);
            SocketHandle handle = objArg(cx, this, args, 0, SocketHandle.class, false);
            if (handle == null) {
                handle = new NIOSocketHandle(runner);
            }

            return new TCPImpl(handle, runner);
        }

        @Override
        protected Object getInstanceIdValue(int id)
        {
            switch (id) {
            case Id_onconnection:
                return onConnection;
            default:
                return super.getInstanceIdValue(id);
            }
        }

        @Override
        protected void setInstanceIdValue(int id, Object value)
        {
            switch (id) {
            case Id_onconnection:
                onConnection = (Function)value;
                break;
            default:
                super.setInstanceIdValue(id, value);
                break;
            }
        }

        @Override
        protected Object prototypeCall(int id, Context cx, Scriptable scope, Object[] args)
        {
            switch (id) {
            case Id_bind:
            case Id_bind6:
                return bind(args);
            case Id_listen:
                return listen(args);
            case Id_connect:
            case Id_connect6:
                return connect(cx, args);
            case Id_shutdown:
                shutdown(cx, args);
                break;
            case Id_getsockname:
                getsockname(cx, args);
                break;
            case Id_getpeername:
                getpeername(cx, args);
                break;
            case Id_setnodelay:
                setNoDelay(cx, args);
                break;
            case Id_setkeepalive:
                setKeepAlive(cx, args);
                break;
            default:
                return super.prototypeCall(id, cx, scope, args);
            }
            return Undefined.instance;
        }

        private Object bind(Object[] args)
        {
            String address = stringArg(args, 0);
            int port = intArg(args, 1);
            try {
                sockHandle.bind(address, port);
                return Undefined.instance;
            } catch (OSException ose) {
                return ose.getCode();
            }
        }

        private Object listen(Object[] args)
        {
            int backlog = intArg(args, 0);
            try {
                sockHandle.listen(backlog, new IOCompletionHandler()
                {
                    @Override
                    public void ioComplete(int errCode, AbstractHandle value)
                    {
                        onConnection(errCode, value);
                    }
                });
                pinState.incrementPinRequest(runtime);
                return Undefined.instance;
            } catch (OSException ose) {
                return ose.getCode();
            }
        }

        protected void onConnection(int errCode, AbstractHandle handle)
        {
            Context cx = Context.getCurrentContext();
            if (onConnection != null) {
                TCPImpl sock = (TCPImpl)cx.newObject(this, CLASS_NAME, new Object[] { handle });
                onConnection.call(cx, onConnection, this,
                                  new Object[] { (errCode == 0 ? Undefined.instance : errCode), sock });
                sock.pinState.incrementPinRequest(runtime);
            }
        }

        private Object connect(Context cx, Object[] args)
        {
            final ConnectImpl req = objArg(cx, this, args, 0, ConnectImpl.class, true);
            String host = stringArg(args, 1);
            int port = intArg(args, 2);

            try {
                sockHandle.connect(host, port, new IOCompletionHandler()
                {
                    @Override
                    public void ioComplete(int errCode, Integer value)
                    {
                        pinState.decrementPinRequest(runtime);
                        req.callOnComplete(Context.getCurrentContext(), TCPWrap.TCPImpl.this, errCode);
                    }
                });
                pinState.incrementPinRequest(runtime);
            } catch (OSException ose) {
                return ose.getCode();
            }
            return Undefined.instance;
        }

        private void shutdown(Context cx, Object[] args)
        {
            final StreamWrap.ShutdownWrap req = objArg(cx, this, args, 0, StreamWrap.ShutdownWrap.class, true);
            final TCPImpl self = this;

            sockHandle.shutdown(new IOCompletionHandler()
            {
                @Override
                public void ioComplete(int errCode, Integer value)
                {
                    req.callOnComplete(Context.getCurrentContext(), self, self, errCode);
                }
            });
        }

        private void getsockname(Context cx, Object[] args)
        {
            Scriptable out = objArg(cx, this, args, 0, Scriptable.class, true);
            InetSocketAddress addr = sockHandle.getSockName();
            if (addr != null) {
                formatAddress(addr, out);
            }
        }

        private void getpeername(Context cx, Object[] args)
        {
            Scriptable out = objArg(cx, this, args, 0, Scriptable.class, true);
            InetSocketAddress addr = sockHandle.getPeerName();
            if (addr != null) {
                formatAddress(addr, out);
            }
        }

        private void formatAddress(InetSocketAddress addr, Scriptable out)
        {
            out.put("port", out, addr.getPort());
            out.put("address", out, addr.getAddress().getHostAddress());
            if (addr.getAddress() instanceof Inet6Address) {
                out.put("family", out, "IPv6");
            } else {
                out.put("family", out, "IPv4");
            }
        }

        private void setNoDelay(Context cx, Object[] args)
        {
            boolean nd = booleanArg(args, 0);
            try {
                sockHandle.setNoDelay(nd);
            } catch (OSException ose) {
                throw Utils.makeError(cx, this, ose);
            }
        }

        private void setKeepAlive(Context cx, Object[] args)
        {
            boolean nd = booleanArg(args, 0);
            try {
                sockHandle.setKeepAlive(nd);
            } catch (OSException ose) {
                throw Utils.makeError(cx, this, ose);
            }
        }
    }

    public static class ConnectImpl
        extends AbstractIdObject
    {
        public static final String CLASS_NAME = "TCPConnectWrap";

        private static final IdPropertyMap props = new IdPropertyMap(CLASS_NAME);

        private static final int
            Id_oncomplete = 1;

        static {
            props.addProperty("oncomplete", Id_oncomplete, 0);
        }

        private Function onComplete;

        public ConnectImpl()
        {
            super(props);
        }

        @Override
        protected ConnectImpl defaultConstructor()
        {
            return new ConnectImpl();
        }

        @Override
        protected Object getInstanceIdValue(int id)
        {
            switch (id) {
            case Id_oncomplete:
                return onComplete;
            default:
                return super.getInstanceIdValue(id);
            }
        }

        @Override
        protected void setInstanceIdValue(int id, Object value)
        {
            switch (id) {
            case Id_oncomplete:
                onComplete = (Function)value;
                break;
            default:
                super.setInstanceIdValue(id, value);
                break;
            }
        }

        public void callOnComplete(Context cx, JavaStreamWrap.StreamWrapImpl handle, int err)
        {
            if ((onComplete == null) || Undefined.instance.equals(onComplete)) {
                return;
            }

            boolean rw = (err == 0);
            // This one wants err to be "0" for success, unlike "undefined" for some other ones
            onComplete.call(cx, onComplete, handle,
                            new Object[] {
                                err, handle, this, rw, rw
                            });
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy