Package org.jruby.ext.socket

Source Code of org.jruby.ext.socket.RubyBasicSocket

/***** BEGIN LICENSE BLOCK *****
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Common Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/cpl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2007 Ola Bini <ola@ologix.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the CPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the CPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.ext.socket;

import java.io.EOFException;
import java.io.IOException;
import java.nio.channels.Channel;
import java.nio.channels.SocketChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.DatagramChannel;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import org.jruby.util.io.OpenFile;
import org.jruby.Ruby;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyNumeric;
import org.jruby.RubyIO;
import org.jruby.RubyString;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.io.BadDescriptorException;
import org.jruby.util.io.ChannelStream;
import org.jruby.util.io.ModeFlags;
import org.jruby.util.io.ChannelDescriptor;

/**
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
*/
@JRubyClass(name="BasicSocket", parent="IO")
public class RubyBasicSocket extends RubyIO {
    private static ObjectAllocator BASICSOCKET_ALLOCATOR = new ObjectAllocator() {
        public IRubyObject allocate(Ruby runtime, RubyClass klass) {
            return new RubyBasicSocket(runtime, klass);
        }
    };

    static void createBasicSocket(Ruby runtime) {
        RubyClass rb_cBasicSocket = runtime.defineClass("BasicSocket", runtime.getIO(), BASICSOCKET_ALLOCATOR);

        rb_cBasicSocket.defineAnnotatedMethods(RubyBasicSocket.class);
    }

    public RubyBasicSocket(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
   
    protected void initSocket(Ruby runtime, ChannelDescriptor descriptor) {
        // make sure descriptor is registered
        registerDescriptor(descriptor);
       
        // continue with normal initialization
        openFile = new OpenFile();
       
        try {
            openFile.setMainStream(ChannelStream.fdopen(runtime, descriptor, new ModeFlags(ModeFlags.RDONLY)));
            openFile.setPipeStream(ChannelStream.fdopen(runtime, descriptor, new ModeFlags(ModeFlags.WRONLY)));
            openFile.getPipeStream().setSync(true);
        } catch (org.jruby.util.io.InvalidValueException ex) {
            throw runtime.newErrnoEINVALError();
        }
        openFile.setMode(OpenFile.READWRITE | OpenFile.SYNC);
    }

    @Override
    public IRubyObject close_write(ThreadContext context) {
        if (context.getRuntime().getSafeLevel() >= 4 && isTaint()) {
            throw context.getRuntime().newSecurityError("Insecure: can't close");
        }
       
        if (openFile.getPipeStream() == null && openFile.isReadable()) {
            throw context.getRuntime().newIOError("closing non-duplex IO for writing");
        }
       
        if (!openFile.isReadable()) {
            close();
        } else {
            Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
            if (socketChannel instanceof SocketChannel
                    || socketChannel instanceof DatagramChannel) {
                try {
                    asSocket().shutdownOutput();
                } catch (IOException e) {
                    throw context.getRuntime().newIOError(e.getMessage());
                }
            }
            openFile.setPipeStream(null);
            openFile.setMode(openFile.getMode() & ~OpenFile.WRITABLE);
        }
        return context.getRuntime().getNil();
    }
   
    @Override
    public IRubyObject close_read(ThreadContext context) {
        Ruby runtime = context.getRuntime();
        if (runtime.getSafeLevel() >= 4 && isTaint()) {
            throw runtime.newSecurityError("Insecure: can't close");
        }

        if (!openFile.isWritable()) {
            close();
        } else {
            if(openFile.getPipeStream() != null) {
                Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
                if (socketChannel instanceof SocketChannel
                    || socketChannel instanceof DatagramChannel) {
                    try {
                        asSocket().shutdownInput();
                    } catch (IOException e) {
                        throw runtime.newIOError(e.getMessage());
                    }
                }
                openFile.setMainStream(openFile.getPipeStream());
                openFile.setPipeStream(null);
                openFile.setMode(openFile.getMode() & ~OpenFile.READABLE);
            }
        }
        return runtime.getNil();
    }

    @JRubyMethod(name = "send", rest = true)
    public IRubyObject write_send(ThreadContext context, IRubyObject[] args) {
        return syswrite(context, args[0]);
    }

    @Deprecated
    public IRubyObject recv(IRubyObject[] args) {
        return recv(getRuntime().getCurrentContext(), args);
    }
    @JRubyMethod(rest = true)
    public IRubyObject recv(ThreadContext context, IRubyObject[] args) {
        OpenFile openFile = getOpenFileChecked();
        try {
            return RubyString.newString(context.getRuntime(), openFile.getMainStream().read(RubyNumeric.fix2int(args[0])));
        } catch (BadDescriptorException e) {
            throw context.getRuntime().newErrnoEBADFError();
        } catch (EOFException e) {
            // recv returns nil on EOF
            return context.getRuntime().getNil();
      } catch (IOException e) {
            // All errors to sysread should be SystemCallErrors, but on a closed stream
            // Ruby returns an IOError.  Java throws same exception for all errors so
            // we resort to this hack...
            if ("Socket not open".equals(e.getMessage())) {
              throw context.getRuntime().newIOError(e.getMessage());
            }
          throw context.getRuntime().newSystemCallError(e.getMessage());
      }
    }

    protected InetSocketAddress getLocalSocket() {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if (socketChannel instanceof SocketChannel) {
            return (InetSocketAddress)((SocketChannel)socketChannel).socket().getLocalSocketAddress();
        } else if (socketChannel instanceof ServerSocketChannel) {
            return (InetSocketAddress)((ServerSocketChannel) socketChannel).socket().getLocalSocketAddress();
        } else if (socketChannel instanceof DatagramChannel) {
            return (InetSocketAddress)((DatagramChannel) socketChannel).socket().getLocalSocketAddress();
        } else {
            return null;
        }
    }

    protected InetSocketAddress getRemoteSocket() {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof SocketChannel) {
            return (InetSocketAddress)((SocketChannel)socketChannel).socket().getRemoteSocketAddress();
        } else {
            return null;
        }
    }

    private Socket asSocket() {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(!(socketChannel instanceof SocketChannel)) {
            throw getRuntime().newErrnoENOPROTOOPTError();
        }

        return ((SocketChannel)socketChannel).socket();
    }

    private ServerSocket asServerSocket() {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(!(socketChannel instanceof ServerSocketChannel)) {
            throw getRuntime().newErrnoENOPROTOOPTError();
        }

        return ((ServerSocketChannel)socketChannel).socket();
    }

    private DatagramSocket asDatagramSocket() {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(!(socketChannel instanceof DatagramChannel)) {
            throw getRuntime().newErrnoENOPROTOOPTError();
        }

        return ((DatagramChannel)socketChannel).socket();
    }

    private IRubyObject getBroadcast(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return trueFalse(runtime, (socketChannel instanceof DatagramChannel) ? asDatagramSocket().getBroadcast() : false);
    }

    private void setBroadcast(IRubyObject val) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof DatagramChannel) {
            asDatagramSocket().setBroadcast(asBoolean(val));
        }
    }

    private void setKeepAlive(IRubyObject val) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof SocketChannel) {
            asSocket().setKeepAlive(asBoolean(val));
        }
    }

    private void setReuseAddr(IRubyObject val) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof ServerSocketChannel) {
            asServerSocket().setReuseAddress(asBoolean(val));
        }
    }

    private void setRcvBuf(IRubyObject val) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof SocketChannel) {
            asSocket().setReceiveBufferSize(asNumber(val));
        } else if(socketChannel instanceof ServerSocketChannel) {
            asServerSocket().setReceiveBufferSize(asNumber(val));
        } else if(socketChannel instanceof DatagramChannel) {
            asDatagramSocket().setReceiveBufferSize(asNumber(val));
        }
    }

    private void setTimeout(IRubyObject val) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof SocketChannel) {
            asSocket().setSoTimeout(asNumber(val));
        } else if(socketChannel instanceof ServerSocketChannel) {
            asServerSocket().setSoTimeout(asNumber(val));
        } else if(socketChannel instanceof DatagramChannel) {
            asDatagramSocket().setSoTimeout(asNumber(val));
        }
    }

    private void setSndBuf(IRubyObject val) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof SocketChannel) {
            asSocket().setSendBufferSize(asNumber(val));
        } else if(socketChannel instanceof DatagramChannel) {
            asDatagramSocket().setSendBufferSize(asNumber(val));
        }
    }

    private void setLinger(IRubyObject val) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof SocketChannel) {
            if(val instanceof RubyBoolean && !val.isTrue()) {
                asSocket().setSoLinger(false, 0);
            } else {
                int num = asNumber(val);
                if(num == -1) {
                    asSocket().setSoLinger(false, 0);
                } else {
                    asSocket().setSoLinger(true, num);
                }
            }
        }
    }

    private void setOOBInline(IRubyObject val) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        if(socketChannel instanceof SocketChannel) {
            asSocket().setOOBInline(asBoolean(val));
        }
    }

    private int asNumber(IRubyObject val) {
        if(val instanceof RubyNumeric) {
            return RubyNumeric.fix2int(val);
        } else {
            return stringAsNumber(val);
        }
    }

    private int stringAsNumber(IRubyObject val) {
        String str = val.convertToString().toString();
        int res = 0;
        res += (str.charAt(0)<<24);
        res += (str.charAt(1)<<16);
        res += (str.charAt(2)<<8);
        res += (str.charAt(3));
        return res;
    }

    protected boolean asBoolean(IRubyObject val) {
        if(val instanceof RubyString) {
            return stringAsNumber(val) != 0;
        } else if(val instanceof RubyNumeric) {
            return RubyNumeric.fix2int(val) != 0;
        } else {
            return val.isTrue();
        }
    }

    private IRubyObject getKeepAlive(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return trueFalse(runtime,
                         (socketChannel instanceof SocketChannel) ? asSocket().getKeepAlive() : false
                         );
    }

    private IRubyObject getLinger(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return number(runtime,
                      (socketChannel instanceof SocketChannel) ? asSocket().getSoLinger() : 0
                      );
    }

    private IRubyObject getOOBInline(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return trueFalse(runtime,
                         (socketChannel instanceof SocketChannel) ? asSocket().getOOBInline() : false
                         );
    }

    private IRubyObject getRcvBuf(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return number(runtime,
                      (socketChannel instanceof SocketChannel) ? asSocket().getReceiveBufferSize() :
                      ((socketChannel instanceof ServerSocketChannel) ? asServerSocket().getReceiveBufferSize() :
                       asDatagramSocket().getReceiveBufferSize())
                      );
    }

    private IRubyObject getSndBuf(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return number(runtime,
                      (socketChannel instanceof SocketChannel) ? asSocket().getSendBufferSize() :
                      ((socketChannel instanceof DatagramChannel) ? asDatagramSocket().getSendBufferSize() : 0)
                      );
    }

    private IRubyObject getReuseAddr(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return trueFalse(runtime,
                         (socketChannel instanceof ServerSocketChannel) ? asServerSocket().getReuseAddress() : false
                         );
    }

    private IRubyObject getTimeout(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return number(runtime,
                      (socketChannel instanceof SocketChannel) ? asSocket().getSoTimeout() :
                      ((socketChannel instanceof ServerSocketChannel) ? asServerSocket().getSoTimeout() :
                       ((socketChannel instanceof DatagramChannel) ? asDatagramSocket().getSoTimeout() : 0))
                      );
    }

    protected int getSoTypeDefault() {
        return 0;
    }

    private IRubyObject getSoType(Ruby runtime) throws IOException {
        Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel();
        return number(runtime,
                      (socketChannel instanceof SocketChannel) ? RubySocket.SOCK_STREAM :
                      ((socketChannel instanceof ServerSocketChannel) ? RubySocket.SOCK_STREAM :
                       ((socketChannel instanceof DatagramChannel) ? RubySocket.SOCK_DGRAM : getSoTypeDefault()))
                      );
    }

    private IRubyObject trueFalse(Ruby runtime, boolean val) {
        return runtime.newString( val ? " \u0000\u0000\u0000" : "\u0000\u0000\u0000\u0000" );
    }

    private IRubyObject number(Ruby runtime, long s) {
        StringBuilder result = new StringBuilder();
        result.append((char) ((s>>24) &0xff)).append((char) ((s>>16) &0xff));
        result.append((char) ((s >> 8) & 0xff)).append((char) (s & 0xff));
        return runtime.newString(result.toString());
    }
    @Deprecated
    public IRubyObject getsockopt(IRubyObject lev, IRubyObject optname) {
        return getsockopt(getRuntime().getCurrentContext(), lev, optname);
    }
    @JRubyMethod
    public IRubyObject getsockopt(ThreadContext context, IRubyObject lev, IRubyObject optname) {
        int level = RubyNumeric.fix2int(lev);
        int opt = RubyNumeric.fix2int(optname);
        Ruby runtime = context.getRuntime();

        try {
            switch(level) {
            case RubySocket.SOL_IP:
            case RubySocket.SOL_SOCKET:
            case RubySocket.SOL_TCP:
            case RubySocket.SOL_UDP:
                switch(opt) {
                case RubySocket.SO_BROADCAST:
                    return getBroadcast(runtime);
                case RubySocket.SO_KEEPALIVE:
                    return getKeepAlive(runtime);
                case RubySocket.SO_LINGER:
                    return getLinger(runtime);
                case RubySocket.SO_OOBINLINE:
                    return getOOBInline(runtime);
                case RubySocket.SO_RCVBUF:
                    return getRcvBuf(runtime);
                case RubySocket.SO_REUSEADDR:
                    return getReuseAddr(runtime);
                case RubySocket.SO_SNDBUF:
                    return getSndBuf(runtime);
                case RubySocket.SO_RCVTIMEO:
                case RubySocket.SO_SNDTIMEO:
                    return getTimeout(runtime);
                case RubySocket.SO_TYPE:
                    return getSoType(runtime);

                    // Can't support the rest with Java
                case RubySocket.SO_RCVLOWAT:
                    return number(runtime, 1);
                case RubySocket.SO_SNDLOWAT:
                    return number(runtime, 2048);
                case RubySocket.SO_DEBUG:
                case RubySocket.SO_ERROR:
                case RubySocket.SO_DONTROUTE:
                case RubySocket.SO_TIMESTAMP:
                    return trueFalse(runtime, false);
                default:
                    throw context.getRuntime().newErrnoENOPROTOOPTError();
                }
            default:
                throw context.getRuntime().newErrnoENOPROTOOPTError();
            }
        } catch(IOException e) {
            throw context.getRuntime().newErrnoENOPROTOOPTError();
        }
    }
    @Deprecated
    public IRubyObject setsockopt(IRubyObject lev, IRubyObject optname, IRubyObject val) {
        return setsockopt(getRuntime().getCurrentContext(), lev, optname, val);
    }
    @JRubyMethod
    public IRubyObject setsockopt(ThreadContext context, IRubyObject lev, IRubyObject optname, IRubyObject val) {
        int level = RubyNumeric.fix2int(lev);
        int opt = RubyNumeric.fix2int(optname);

        try {
            switch(level) {
            case RubySocket.SOL_IP:
            case RubySocket.SOL_SOCKET:
            case RubySocket.SOL_TCP:
            case RubySocket.SOL_UDP:
                switch(opt) {
                case RubySocket.SO_BROADCAST:
                    setBroadcast(val);
                    break;
                case RubySocket.SO_KEEPALIVE:
                    setKeepAlive(val);
                    break;
                case RubySocket.SO_LINGER:
                    setLinger(val);
                    break;
                case RubySocket.SO_OOBINLINE:
                    setOOBInline(val);
                    break;
                case RubySocket.SO_RCVBUF:
                    setRcvBuf(val);
                    break;
                case RubySocket.SO_REUSEADDR:
                    setReuseAddr(val);
                    break;
                case RubySocket.SO_SNDBUF:
                    setSndBuf(val);
                    break;
                case RubySocket.SO_RCVTIMEO:
                case RubySocket.SO_SNDTIMEO:
                    setTimeout(val);
                    break;
                    // Can't support the rest with Java
                case RubySocket.SO_TYPE:
                case RubySocket.SO_RCVLOWAT:
                case RubySocket.SO_SNDLOWAT:
                case RubySocket.SO_DEBUG:
                case RubySocket.SO_ERROR:
                case RubySocket.SO_DONTROUTE:
                case RubySocket.SO_TIMESTAMP:
                    break;
                default:
                    throw context.getRuntime().newErrnoENOPROTOOPTError();
                }
                break;
            default:
                throw context.getRuntime().newErrnoENOPROTOOPTError();
            }
        } catch(IOException e) {
            throw context.getRuntime().newErrnoENOPROTOOPTError();
        }
        return context.getRuntime().newFixnum(0);
    }

    @Deprecated
    public IRubyObject getsockname() {
        return getsockname(getRuntime().getCurrentContext());
    }

    @JRubyMethod(name = {"getsockname", "__getsockname"})
    public IRubyObject getsockname(ThreadContext context) {
        SocketAddress sock = getLocalSocket();
        if(null == sock) {
            throw context.getRuntime().newIOError("Not Supported");
        }
        return context.getRuntime().newString(sock.toString());
    }
    @Deprecated
    public IRubyObject getpeername() {
        return getpeername(getRuntime().getCurrentContext());
    }
    @JRubyMethod(name = {"getpeername", "__getpeername"})
    public IRubyObject getpeername(ThreadContext context) {
        SocketAddress sock = getRemoteSocket();
        if(null == sock) {
            throw context.getRuntime().newIOError("Not Supported");
        }
        return context.getRuntime().newString(sock.toString());
    }

    @JRubyMethod(optional = 1)
    public IRubyObject shutdown(ThreadContext context, IRubyObject[] args) {
        if (context.getRuntime().getSafeLevel() >= 4 && tainted_p(context).isFalse()) {
            throw context.getRuntime().newSecurityError("Insecure: can't shutdown socket");
        }
       
        int how = 2;
        if (args.length > 0) {
            how = RubyNumeric.fix2int(args[0]);
        }
        if (how < 0 || 2 < how) {
            throw context.getRuntime().newArgumentError("`how' should be either 0, 1, 2");
        }
        if (how != 2) {
            throw context.getRuntime().newNotImplementedError("Shutdown currently only works with how=2");
        }
        return close();
    }

    @JRubyMethod(meta = true)
    public static IRubyObject do_not_reverse_lookup(IRubyObject recv) {
        return recv.getRuntime().isDoNotReverseLookupEnabled() ? recv.getRuntime().getTrue() : recv.getRuntime().getFalse();
    }
   
    @JRubyMethod(name = "do_not_reverse_lookup=", meta = true)
    public static IRubyObject set_do_not_reverse_lookup(IRubyObject recv, IRubyObject flag) {
        recv.getRuntime().setDoNotReverseLookupEnabled(flag.isTrue());
        return recv.getRuntime().isDoNotReverseLookupEnabled() ? recv.getRuntime().getTrue() : recv.getRuntime().getFalse();
    }
}// RubyBasicSocket
TOP

Related Classes of org.jruby.ext.socket.RubyBasicSocket

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.