Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: EPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Eclipse 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/epl-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 EPL, 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 EPL, the GPL or the LGPL. END LICENSE BLOCK ***
 
 
 package org.jruby.ext.socket;
 
 import static jnr.constants.platform.IPProto.IPPROTO_TCP;
 import static jnr.constants.platform.IPProto.IPPROTO_IP;
 import static jnr.constants.platform.Sock.SOCK_DGRAM;
 import static jnr.constants.platform.Sock.SOCK_STREAM;
 import static jnr.constants.platform.TCP.TCP_NODELAY;
 
 
 import org.jruby.Ruby;
 
Implementation of the BasicSocket class from Ruby.
 
 @JRubyClass(name="BasicSocket", parent="IO")
 public class RubyBasicSocket extends RubyIO {
     static void createBasicSocket(Ruby runtime) {
         RubyClass rb_cBasicSocket = runtime.defineClass("BasicSocket"runtime.getIO(), );
 
         rb_cBasicSocket.defineAnnotatedMethods(RubyBasicSocket.class);
     }
 
     private static ObjectAllocator BASICSOCKET_ALLOCATOR = new ObjectAllocator() {
         public IRubyObject allocate(Ruby runtimeRubyClass klass) {
             return new RubyBasicSocket(runtimeklass);
        }
    };
    public RubyBasicSocket(Ruby runtimeRubyClass type) {
        super(runtimetype);
         = runtime.is1_9();
    }
    @JRubyMethod(meta = true)
    public static IRubyObject for_fd(ThreadContext contextIRubyObject _klassIRubyObject _fileno) {
        Ruby runtime = context.runtime;
        int fileno = (int)_fileno.convertToInteger().getLongValue();
        RubyClass klass = (RubyClass)_klass;
        ChannelDescriptor descriptor = ChannelDescriptor.getDescriptorByFileno(runtime.getFilenoExtMap(fileno));
        RubyBasicSocket basicSocket = (RubyBasicSocket)klass.getAllocator().allocate(runtimeklass);
        basicSocket.initSocket(runtimedescriptor);
        return basicSocket;
    }
    @JRubyMethod(name = "do_not_reverse_lookup", compat = .)
        return context.runtime.newBoolean();
    }
    @JRubyMethod(name = "do_not_reverse_lookup=", compat = .)
         = flag.isTrue();
        return do_not_reverse_lookup19(context);
    }
    @JRubyMethod(meta = true)
    public static IRubyObject do_not_reverse_lookup(ThreadContext contextIRubyObject recv) {
        return context.runtime.newBoolean(context.runtime.isDoNotReverseLookupEnabled());
    }
    @JRubyMethod(name = "do_not_reverse_lookup=", meta = true)
    public static IRubyObject set_do_not_reverse_lookup(ThreadContext contextIRubyObject recvIRubyObject flag) {
        context.runtime.setDoNotReverseLookupEnabled(flag.isTrue());
        return flag;
    }
    @JRubyMethod(name = "send")
    public IRubyObject send(ThreadContext contextIRubyObject _mesgIRubyObject _flags) {
        // TODO: implement flags
        return syswrite(context_mesg);
    }
    @JRubyMethod(name = "send")
    public IRubyObject send(ThreadContext contextIRubyObject _mesgIRubyObject _flagsIRubyObject _to) {
        // TODO: implement flags and to
        return send(context_mesg_flags);
    }
    @Deprecated
    public IRubyObject recv(ThreadContext contextIRubyObject[] args) {
        switch (args.length) {
            case 1:
                return recv(contextargs[0]);
            case 2:
                return recv(contextargs[0], args[1]);
            default:
                Arity.raiseArgumentError(context.runtimeargs, 1, 2);
                return null// not reached
        }
    }
    public IRubyObject recv(ThreadContext contextIRubyObject _length) {
        Ruby runtime = context.runtime;
        ByteList bytes = doReceive(context, RubyNumeric.fix2int(_length));
        if (bytes == nullreturn context.nil;
        return RubyString.newString(runtimebytes);
    }
    
    public IRubyObject recv(ThreadContext contextIRubyObject _lengthIRubyObject _flags) {
        // TODO: implement flags
        return recv(context_length);
    }
    public IRubyObject recv_nonblock(ThreadContext contextIRubyObject _length) {
        Ruby runtime = context.runtime;
        ByteList bytes = doReceiveNonblock(context, RubyNumeric.fix2int(_length));
        if (bytes == null) {
            if (runtime.is1_9()) {
                throw runtime.newErrnoEAGAINReadableError("recvfrom(2)");
            } else {
                throw runtime.newErrnoEAGAINError("recvfrom(2)");
            }
        }
        return RubyString.newString(runtimebytes);
    }
    public IRubyObject recv_nonblock(ThreadContext contextIRubyObject _lengthIRubyObject _flags) {
        // TODO: implement flags
        return recv_nonblock(context_length);
    }
    public IRubyObject getsockopt(ThreadContext contextIRubyObject _levelIRubyObject _opt) {
        Ruby runtime = context.runtime;
        SocketLevel level = levelFromArg(_level);
        SocketOption opt = optionFromArg(_opt);
        int value = 0;
        try {
            Channel channel = getOpenChannel();
            switch(level) {
            case :
            case :
            case :
            case :
                if (opt == .) {
                    throw runtime.newErrnoENOPROTOOPTError();
                }
                value = SocketType.forChannel(channel).getSocketOption(channelopt);
                
                if (runtime.is1_9()) {
                    return new Option(runtime.leveloptvalue);
                } else {
                    return number(runtime, SocketType.forChannel(channel).getSocketOption(channelopt));
                }
            default:
                throw runtime.newErrnoENOPROTOOPTError();
            }
        } catch (BadDescriptorException e) {
            throw runtime.newErrnoEBADFError();
        } catch(IOException e) {
            throw runtime.newErrnoENOPROTOOPTError();
        }
    }
    public IRubyObject setsockopt(ThreadContext contextIRubyObject _levelIRubyObject _optIRubyObject val) {
        Ruby runtime = context.runtime;
        SocketLevel level = levelFromArg(_level);
        SocketOption opt = optionFromArg(_opt);
        try {
            Channel channel = getOpenChannel();
            SocketType socketType = SocketType.forChannel(channel);
            switch(level) {
            case :
            case :
            case :
            case :
                if (opt == .) {
                    if(val instanceof RubyBoolean && !val.isTrue()) {
                        socketType.setSoLinger(channelfalse, 0);
                    } else {
                        int num = asNumber(val);
                        if(num == -1) {
                            socketType.setSoLinger(channelfalse, 0);
                        } else {
                            socketType.setSoLinger(channeltruenum);
                        }
                    }
                } else {
                    socketType.setSocketOption(channeloptasNumber(val));
                }
                break;
            default:
                int intLevel = (int)_level.convertToInteger().getLongValue();
                int intOpt = (int)_opt.convertToInteger().getLongValue();
                if (.intValue() == intLevel && .intValue() == intOpt) {
                    socketType.setTcpNoDelay(channelasBoolean(val));
                } else if (.intValue() == intLevel) {
                    if (. == intOpt) {
                        joinMulticastGroup(val);
                    }
                } else {
                    throw runtime.newErrnoENOPROTOOPTError();
                }
            }
        } catch (BadDescriptorException e) {
            throw runtime.newErrnoEBADFError();
        } catch(IOException e) {
            throw runtime.newErrnoENOPROTOOPTError();
        }
        return runtime.newFixnum(0);
    }
    @JRubyMethod(name = "getsockname")
    public IRubyObject getsockname(ThreadContext context) {
        return getSocknameCommon(context"getsockname");
    }
    @JRubyMethod(name = "getpeername")
    public IRubyObject getpeername(ThreadContext context) {
        Ruby runtime = context.runtime;
        try {
            SocketAddress sock = getRemoteSocket();
            if(null == sock) {
                throw runtime.newIOError("Not Supported");
            }
            return runtime.newString(sock.toString());
        } catch (BadDescriptorException e) {
            throw runtime.newErrnoEBADFError();
        }
    }
    @JRubyMethod(name = "getpeereid", compat = ., notImplemented = true)
    public IRubyObject getpeereid(ThreadContext context) {
        throw context.runtime.newNotImplementedError("getpeereid not implemented");
    }
    @JRubyMethod(compat = .)
    public IRubyObject local_address(ThreadContext context) {
        try {
            InetSocketAddress address = getSocketAddress();
            if (address == null) {
                return context.nil;
            } else {
                return new Addrinfo(context.runtimecontext.runtime.getClass("Addrinfo"), address.getAddress(), address.getPort(), SocketType.forChannel(getChannel()));
            }
        } catch (BadDescriptorException bde) {
            throw context.runtime.newErrnoEBADFError("address unavailable");
        }
    }
    @JRubyMethod(compat = .)
    public IRubyObject remote_address(ThreadContext context) {
        try {
            InetSocketAddress address = getRemoteSocket();
            if (address == null) {
                return context.nil;
            } else {
                return new Addrinfo(context.runtimecontext.runtime.getClass("Addrinfo"), address.getAddress(), address.getPort(), SocketType.forChannel(getChannel()));
            }
        } catch (BadDescriptorException bde) {
            throw context.runtime.newErrnoEBADFError("address unavailable");
        }
    }
    @JRubyMethod(optional = 1)
    public IRubyObject shutdown(ThreadContext contextIRubyObject[] args) {
        int how = 2;
        if (args.length > 0) {
            how = RubyNumeric.fix2int(args[0]);
        }
        try {
            return shutdownInternal(contexthow);
        } catch (BadDescriptorException e) {
            throw context.runtime.newErrnoEBADFError();
        }
    }
    @Override
    public IRubyObject close_write(ThreadContext context) {
        Ruby runtime = context.runtime;
        if (!.isWritable()) {
            return runtime.getNil();
        }
        if (.getPipeStream() == null && .isReadable()) {
            throw runtime.newIOError("closing non-duplex IO for writing");
        }
        if (!.isReadable()) {
            close();
        } else {
            // shutdown write
            try {
                shutdownInternal(context, 1);
            } catch (BadDescriptorException e) {
                throw runtime.newErrnoEBADFError();
            }
        }
        return context.nil;
    }
    @Override
    public IRubyObject close_read(ThreadContext context) {
        Ruby runtime = context.runtime;
        if (!.isOpen()) {
            throw context.runtime.newIOError("not opened for reading");
        }
        if (!.isWritable()) {
            close();
        } else {
            // shutdown read
            try {
                shutdownInternal(context, 0);
            } catch (BadDescriptorException e) {
                throw runtime.newErrnoEBADFError();
            }
        }
        return context.nil;
    }
    @JRubyMethod(rest = true, notImplemented = true, compat = .)
    public IRubyObject sendmsg(ThreadContext contextIRubyObject[] args) {
        throw context.runtime.newNotImplementedError("sendmsg is not implemented");
    }
    @JRubyMethod(rest = true, notImplemented = true, compat = .)
    public IRubyObject sendmsg_nonblock(ThreadContext contextIRubyObject[] args) {
        throw context.runtime.newNotImplementedError("sendmsg_nonblock is not implemented");
    }
    @JRubyMethod(rest = true, notImplemented = true, compat = .)
    public IRubyObject readmsg(ThreadContext contextIRubyObject[] args) {
        throw context.runtime.newNotImplementedError("readmsg is not implemented");
    }
    @JRubyMethod(rest = true, notImplemented = true, compat = .)
    public IRubyObject readmsg_nonblock(ThreadContext contextIRubyObject[] args) {
        throw context.runtime.newNotImplementedError("readmsg_nonblock is not implemented");
    }
    private ByteList doReceive(ThreadContext contextint length) {
        Ruby runtime = context.runtime;
        ByteBuffer buf = ByteBuffer.allocate(length);
        try {
            context.getThread().beforeBlockingCall();
            int read = .getMainStreamSafe().getDescriptor().read(buf);
            if (read == 0) return null;
            return new ByteList(buf.array(), 0, buf.position());
        } catch (BadDescriptorException e) {
            throw runtime.newIOError("bad descriptor");
        } 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 runtime.newIOError(e.getMessage());
            }
            throw runtime.newSystemCallError(e.getMessage());
        } finally {
            context.getThread().afterBlockingCall();
        }
    }
    public ByteList doReceiveNonblock(ThreadContext contextint length) {
        Ruby runtime = context.runtime;
        Channel channel = getChannel();
        if (!(channel instanceof SelectableChannel)) {
            if (runtime.is1_9()) {
                throw runtime.newErrnoEAGAINReadableError(channel.getClass().getName() + " does not support nonblocking");
            } else {
                throw runtime.newErrnoEAGAINError(channel.getClass().getName() + " does not support nonblocking");
            }
        }
        SelectableChannel selectable = (SelectableChannel)channel;
        synchronized (selectable.blockingLock()) {
            boolean oldBlocking = selectable.isBlocking();
            try {
                selectable.configureBlocking(false);
                try {
                    return doReceive(contextlength);
                } finally {
                    selectable.configureBlocking(oldBlocking);
                }
            } catch(IOException e) {
                throw runtime.newIOErrorFromException(e);
            }
        }
    }
    private void joinMulticastGroup(IRubyObject valthrows IOExceptionBadDescriptorException {
        Channel socketChannel = getOpenChannel();
        if(socketChannel instanceof DatagramChannel) {
            if ( == null) {
                 = new MulticastStateManager();
            }
            if (val instanceof RubyString) {
                byte [] ipaddr_buf = val.convertToString().getBytes();
                .addMembership(ipaddr_buf);
            }
        }
    }
        Channel channel = getOpenChannel();
        return (InetSocketAddress)SocketType.forChannel(channel).getLocalSocketAddress(channel);
    }
        Channel channel = getOpenChannel();
        return (InetSocketAddress)SocketType.forChannel(channel).getRemoteSocketAddress(channel);
    }
    protected Sock getDefaultSocketType() {
        return .;
    }
    protected IRubyObject getSocknameCommon(ThreadContext contextString caller) {
        try {
            InetSocketAddress sock = getSocketAddress();
            if(null == sock) {
                return Sockaddr.pack_sockaddr_in(context, 0, "0.0.0.0");
            } else {
               return Sockaddr.pack_sockaddr_in(contextsock);
            }
        } catch (BadDescriptorException e) {
            throw context.runtime.newErrnoEBADFError();
        }
    }
    private IRubyObject shutdownInternal(ThreadContext contextint howthrows BadDescriptorException {
        Ruby runtime = context.runtime;
        Channel channel;
        switch (how) {
        case 0:
            channel = getOpenChannel();
            try {
                SocketType.forChannel(channel).shutdownInput(channel);
            } catch (IOException e) {
                throw runtime.newIOError(e.getMessage());
            }
            if(.getPipeStream() != null) {
                .setMainStream(.getPipeStream());
                .setPipeStream(null);
            }
            .setMode(.getMode() & ~.);
            return RubyFixnum.zero(runtime);
        case 1:
            channel = getOpenChannel();
            try {
                SocketType.forChannel(channel).shutdownOutput(channel);
            } catch (IOException e) {
                throw runtime.newIOError(e.getMessage());
            }
            .setPipeStream(null);
            .setMode(.getMode() & ~.);
            return RubyFixnum.zero(runtime);
        case 2:
            shutdownInternal(context, 0);
            shutdownInternal(context, 1);
            return RubyFixnum.zero(runtime);
            default:
            throw runtime.newArgumentError("`how' should be either 0, 1, 2");
        }
    }
    public boolean doNotReverseLookup(ThreadContext context) {
        return context.runtime.isDoNotReverseLookupEnabled() || ;
    }
    protected void initSocket(Ruby runtimeChannelDescriptor descriptor) {
        // continue with normal initialization
        MakeOpenFile();
        try {
            .setMainStream(ChannelStream.fdopen(runtimedescriptornewModeFlags(runtime.)));
            .setPipeStream(ChannelStream.fdopen(runtimedescriptornewModeFlags(runtime.)));
            .getPipeStream().setSync(true);
        } catch (org.jruby.util.io.InvalidValueException ex) {
            throw runtime.newErrnoEINVALError();
        }
        // see rsock_init_sock in MRI; sockets are initialized to binary
        setAscii8bitBinmode();
    }
    
    private Channel getOpenChannel() throws BadDescriptorException {
    }
    private int asNumber(IRubyObject val) {
        if (val instanceof RubyNumeric) {
            return RubyNumeric.fix2int(val);
        } else if (val instanceof RubyBoolean) {
            return val.isTrue() ? 1 : 0;
        }
        else {
            return stringAsNumber(val);
        }
    }
    private int stringAsNumber(IRubyObject val) {
        ByteList str = val.convertToString().getByteList();
        IRubyObject res = Pack.unpack(getRuntime(), str).entry(0);
        if (res.isNil()) {
            throw getRuntime().newErrnoEINVALError();
        }
        return RubyNumeric.fix2int(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 static IRubyObject number(Ruby runtimeint s) {
        return RubyString.newString(runtime, Pack.packInt_i(new ByteList(4), s));
    }
    protected static SocketOption optionFromArg(IRubyObject _opt) {
        SocketOption opt;
        if (_opt instanceof RubyString || _opt instanceof RubySymbol) {
            opt = SocketOption.valueOf("SO_" + _opt.toString());
        } else {
            opt = SocketOption.valueOf(RubyNumeric.fix2int(_opt));
        }
        return opt;
    }
    protected static SocketLevel levelFromArg(IRubyObject _level) {
        SocketLevel level;
        if (_level instanceof RubyString || _level instanceof RubySymbol) {
            level = SocketLevel.valueOf("SOL_" + _level.toString());
        } else {
            level = SocketLevel.valueOf(RubyNumeric.fix2int(_level));
        }
        return level;
    }
    protected IRubyObject addrFor(ThreadContext contextInetSocketAddress addrboolean reverse) {
        Ruby r = context.runtime;
        IRubyObject[] ret = new IRubyObject[4];
        if (addr.getAddress() instanceof Inet6Address) {
            ret[0] = r.newString("AF_INET6");
        } else {
            ret[0] = r.newString("AF_INET");
        }
        ret[1] = r.newFixnum(addr.getPort());
        String hostAddress = addr.getAddress().getHostAddress();
        if (!reverse || doNotReverseLookup(context)) {
            ret[2] = r.newString(hostAddress);
        } else {
            ret[2] = r.newString(addr.getHostName());
        }
        ret[3] = r.newString(hostAddress);
        return r.newArrayNoCopy(ret);
    }
    @Deprecated
    public IRubyObject recv(IRubyObject[] args) {
        return recv(getRuntime().getCurrentContext(), args);
    }
    @Deprecated
    public IRubyObject getsockopt(IRubyObject levIRubyObject optname) {
        return getsockopt(getRuntime().getCurrentContext(), levoptname);
    }
    @Deprecated
    public IRubyObject setsockopt(IRubyObject levIRubyObject optnameIRubyObject val) {
        return setsockopt(getRuntime().getCurrentContext(), levoptnameval);
    }
    @Deprecated
    public IRubyObject getsockname() {
        return getsockname(getRuntime().getCurrentContext());
    }
    @Deprecated
    public IRubyObject getpeername() {
        return getpeername(getRuntime().getCurrentContext());
    }
    @Deprecated
    public static IRubyObject do_not_reverse_lookup(IRubyObject recv) {
        return do_not_reverse_lookup(recv.getRuntime().getCurrentContext(), recv);
    }
    @Deprecated
    public static IRubyObject set_do_not_reverse_lookup(IRubyObject recvIRubyObject flag) {
        return set_do_not_reverse_lookup(recv.getRuntime().getCurrentContext(), recvflag);
    }
    private static final ByteList FORMAT_SMALL_I = new ByteList(ByteList.plain("i"));
    protected MulticastStateManager multicastStateManager = null;
    // By default we always reverse lookup unless do_not_reverse_lookup set.
    private boolean doNotReverseLookup = false;
}// RubyBasicSocket