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 org.jruby.Ruby;
 

Author(s):
Ola Bini
 
 @JRubyClass(name="Socket", parent="BasicSocket", include="Socket::Constants")
 public class RubySocket extends RubyBasicSocket {
     static void createSocket(Ruby runtime) {
         RubyClass rb_cSocket = runtime.defineClass("Socket"runtime.getClass("BasicSocket"), );
 
         RubyModule rb_mConstants = rb_cSocket.defineModuleUnder("Constants");
         // we don't have to define any that we don't support; see socket.c
 
         runtime.loadConstantSet(rb_mConstantsSock.class);
         runtime.loadConstantSet(rb_mConstantsSocketOption.class);
         runtime.loadConstantSet(rb_mConstantsSocketLevel.class);
         runtime.loadConstantSet(rb_mConstantsProtocolFamily.class);
         runtime.loadConstantSet(rb_mConstantsAddressFamily.class);
         runtime.loadConstantSet(rb_mConstantsINAddr.class);
         runtime.loadConstantSet(rb_mConstantsIPProto.class);
         runtime.loadConstantSet(rb_mConstantsShutdown.class);
         runtime.loadConstantSet(rb_mConstantsTCP.class);
         runtime.loadConstantSet(rb_mConstantsNameInfo.class);
 
         // this value seems to be hardcoded in MRI to 5 when not defined, but
         // it is 128 on OS X. We use 128 for now until we can get it added to
         // jnr-constants.
         rb_mConstants.setConstant("SOMAXCONN", RubyFixnum.newFixnum(runtime, 128));
 
        // mandatory constants we haven't implemented
        rb_mConstants.setConstant("MSG_OOB"runtime.newFixnum());
        rb_mConstants.setConstant("MSG_PEEK"runtime.newFixnum());
        rb_mConstants.setConstant("MSG_DONTROUTE"runtime.newFixnum());
        rb_mConstants.setConstant("MSG_WAITALL"runtime.newFixnum());
        // constants webrick crashes without
        rb_mConstants.setConstant("AI_PASSIVE"runtime.newFixnum(1));
        // More constants needed by specs
        rb_mConstants.setConstant("IP_MULTICAST_TTL"runtime.newFixnum(10));
        rb_mConstants.setConstant("IP_MULTICAST_LOOP"runtime.newFixnum(11));
        rb_mConstants.setConstant("IP_ADD_MEMBERSHIP"runtime.newFixnum(12));
        rb_mConstants.setConstant("IP_MAX_MEMBERSHIPS"runtime.newFixnum(20));
        rb_mConstants.setConstant("IP_DEFAULT_MULTICAST_LOOP"runtime.newFixnum(1));
        rb_mConstants.setConstant("IP_DEFAULT_MULTICAST_TTL"runtime.newFixnum(1));
        rb_cSocket.includeModule(rb_mConstants);
        rb_cSocket.defineAnnotatedMethods(RubySocket.class);
    }
    private static ObjectAllocator SOCKET_ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtimeRubyClass klass) {
            return new RubySocket(runtimeklass);
        }
    };
    public RubySocket(Ruby runtimeRubyClass type) {
        super(runtimetype);
    }
    @JRubyMethod(meta = true)
    public static IRubyObject for_fd(ThreadContext contextIRubyObject socketClassIRubyObject fd) {
        Ruby runtime = context.runtime;
        if (fd instanceof RubyFixnum) {
            int intFD = (int)((RubyFixnum)fd).getLongValue();
            ChannelDescriptor descriptor = ChannelDescriptor.getDescriptorByFileno(intFD);
            if (descriptor == null) {
                throw runtime.newErrnoEBADFError();
            }
            RubySocket socket = (RubySocket)((RubyClass)socketClass).allocate();
            socket.initFieldsFromDescriptor(runtimedescriptor);
            socket.initSocket(runtimedescriptor);
            return socket;
        } else {
            throw runtime.newTypeError(fdcontext.runtime.getFixnum());
        }
    }
    @JRubyMethod(compat = .)
    public IRubyObject initialize(ThreadContext contextIRubyObject domainIRubyObject typeIRubyObject protocol) {
        Ruby runtime = context.runtime;
        initFieldsFromArgs(runtimedomaintypeprotocol);
        ChannelDescriptor descriptor = initChannel(runtime);
        initSocket(runtimedescriptor);
        return this;
    }
    @JRubyMethod(name = "initialize", compat = .)
    public IRubyObject initialize19(ThreadContext contextIRubyObject domainIRubyObject type) {
        Ruby runtime = context.runtime;
        initFieldsFromArgs(runtimedomaintype);
        ChannelDescriptor descriptor = initChannel(runtime);
        initSocket(runtimedescriptor);
        return this;
    }
    @JRubyMethod(name = "initialize", compat = .)
    public IRubyObject initialize19(ThreadContext contextIRubyObject domainIRubyObject typeIRubyObject protocol) {
        Ruby runtime = context.runtime;
        initFieldsFromArgs(runtimedomaintypeprotocol);
        ChannelDescriptor descriptor = initChannel(runtime);
        initSocket(runtimedescriptor);
        return this;
    }
    @JRubyMethod()
    public IRubyObject connect_nonblock(ThreadContext contextIRubyObject arg) {
        SocketAddress addr = addressForChannel(contextarg);
        doConnectNonblock(contextgetChannel(), addr);
        return RubyFixnum.zero(context.runtime);
    }
    @JRubyMethod()
    public IRubyObject connect(ThreadContext contextIRubyObject arg) {
        SocketAddress addr = addressForChannel(contextarg);
        doConnect(contextgetChannel(), addr);
        return RubyFixnum.zero(context.runtime);
    }
    @JRubyMethod()
    public IRubyObject bind(ThreadContext contextIRubyObject arg) {
        InetSocketAddress iaddr = Sockaddr.addressFromArg(contextarg);
        doBind(contextgetChannel(), iaddr);
        return RubyFixnum.zero(context.runtime);
    }
    public IRubyObject recvfrom(ThreadContext contextIRubyObject length) {
        return super.recv(contextlength);
    }
    public IRubyObject recvfrom(ThreadContext contextIRubyObject lengthIRubyObject flags) {
        return super.recv(contextlengthflags);
    }
    public IRubyObject recvfrom_nonblock(ThreadContext contextIRubyObject length) {
        return super.recv_nonblock(contextlength);
    }
    public IRubyObject recvfrom_nonblock(ThreadContext contextIRubyObject lengthIRubyObject flags) {
        return super.recv_nonblock(contextlengthflags);
    }
    @JRubyMethod(notImplemented = true)
    public IRubyObject listen(ThreadContext contextIRubyObject backlog) {
        throw SocketUtils.sockerr(context.runtime);
    }
    @JRubyMethod(notImplemented = true)
    public IRubyObject accept(ThreadContext context) {
        throw SocketUtils.sockerr(context.runtime);
    }
    @JRubyMethod(meta = true)
    public static IRubyObject gethostname(ThreadContext contextIRubyObject recv) {
        return SocketUtils.gethostname(context);
    }
    @JRubyMethod(required = 1, rest = true, meta = true)
    public static IRubyObject gethostbyaddr(ThreadContext contextIRubyObject recvIRubyObject[] args) {
        return SocketUtils.gethostbyaddr(contextargs);
    }
    @JRubyMethod(required = 1, optional = 1, meta = true)
    public static IRubyObject getservbyname(ThreadContext contextIRubyObject recvIRubyObject[] args) {
        return SocketUtils.getservbyname(contextargs);
    }
    @JRubyMethod(name = {"pack_sockaddr_in""sockaddr_in"}, meta = true)
    public static IRubyObject pack_sockaddr_in(ThreadContext contextIRubyObject recvIRubyObject portIRubyObject host) {
        return SocketUtils.pack_sockaddr_in(contextporthost);
    }
    @JRubyMethod(meta = true)
    public static IRubyObject unpack_sockaddr_in(ThreadContext contextIRubyObject recvIRubyObject addr) {
        return Sockaddr.unpack_sockaddr_in(contextaddr);
    }
    @JRubyMethod(name = {"pack_sockaddr_un""sockaddr_un"}, meta = true)
    public static IRubyObject pack_sockaddr_un(ThreadContext contextIRubyObject recvIRubyObject filename) {
        return SocketUtils.pack_sockaddr_un(contextfilename);
    }
    @JRubyMethod(meta = true)
    public static IRubyObject gethostbyname(ThreadContext contextIRubyObject recvIRubyObject hostname) {
        return SocketUtils.gethostbyname(contexthostname);
    }
    @JRubyMethod(required = 2, optional = 4, meta = true)
    public static IRubyObject getaddrinfo(ThreadContext contextIRubyObject recvIRubyObject[] args) {
        return SocketUtils.getaddrinfo(contextargs);
    }
    @JRubyMethod(required = 1, optional = 1, meta = true)
    public static IRubyObject getnameinfo(ThreadContext contextIRubyObject recvIRubyObject[] args) {
        return SocketUtils.getnameinfo(contextargs);
    }
    @JRubyMethod(meta = true, compat = .)
    public static IRubyObject ip_address_list(ThreadContext contextIRubyObject self) {
        return SocketUtils.ip_address_list(context);
    }
    @Override
    protected Sock getDefaultSocketType() {
        return ;
    }
    private void initFieldsFromDescriptor(Ruby runtimeChannelDescriptor descriptor) {
        Channel mainChannel = descriptor.getChannel();
        if (mainChannel instanceof SocketChannel) {
            // ok, it's a socket...set values accordingly
            // just using AF_INET since we can't tell from SocketChannel...
             = .;
             = .;
             = .;
        } else if (mainChannel instanceof UnixSocketChannel) {
             = .;
             = .;
             = .;
        } else if (mainChannel instanceof DatagramChannel) {
            // datagram, set accordingly
            // again, AF_INET
             = .;
             = .;
             = .;
        } else {
            throw runtime.newErrnoENOTSOCKError("can't Socket.new/for_fd against a non-socket");
        }
    }
    private void initFieldsFromArgs(Ruby runtimeIRubyObject domainIRubyObject typeIRubyObject protocol) {
        initDomain(runtimedomain);
        initType(runtimetype);
        initProtocol(runtimeprotocol);
    }
    private void initFieldsFromArgs(Ruby runtimeIRubyObject domainIRubyObject type) {
        initDomain(runtimedomain);
        initType(runtimetype);
    }
    protected void initFromServer(Ruby runtimeRubyServerSocket serverSocketSocketChannel socketChannel) {
         = serverSocket.soDomain;
         = serverSocket.soType;
         = serverSocket.soProtocol;
        initSocket(runtimenewChannelDescriptor(runtimesocketChannel));
    }
    protected ChannelDescriptor initChannel(Ruby runtime) {
        Channel channel;
        try {
            if( == .) {
                if ( == . ||
                         == .) {
                    channel = UnixSocketChannel.open();
                } else if ( == . ||
                         == . ||
                         == .) {
                    channel = SocketChannel.open();
                } else {
                    throw runtime.newArgumentError("unsupported protocol family `" +  + "'");
                }
            } else if( == .) {
                channel = DatagramChannel.open();
            } else {
                throw runtime.newArgumentError("unsupported socket type `" +  + "'");
            }
            return newChannelDescriptor(runtimechannel);
        } catch(IOException e) {
            throw SocketUtils.sockerr(runtime"initialize: " + e.toString());
        }
    }
    protected static ChannelDescriptor newChannelDescriptor(Ruby runtimeChannel channel) {
        ModeFlags modeFlags = newModeFlags(runtime.);
        return new ChannelDescriptor(channelmodeFlags);
    }
    private void initProtocol(Ruby runtimeIRubyObject protocol) {
        ProtocolFamily protocolFamily = SocketUtils.protocolFamilyFromArg(protocol);
        if (protocolFamily == null) {
            return// no protocol specified, ignore it
        }
         = protocolFamily;
    }
    private void initType(Ruby runtimeIRubyObject type) {
        Sock sockType = SocketUtils.sockFromArg(type);
        if (sockType == null) {
            throw SocketUtils.sockerr(runtime"unknown socket type " + type);
        }
         = sockType;
    }
    private void initDomain(Ruby runtimeIRubyObject domain) {
        AddressFamily family = SocketUtils.addressFamilyFromArg(domain);
        if (family == null) {
            throw SocketUtils.sockerr(runtime"unknown socket domain " + domain);
        }
         = family;
         = ProtocolFamily.valueOf("PF" + .name().substring(2));
    }
    private void doConnectNonblock(ThreadContext contextChannel channelSocketAddress addr) {
        if (!(channel instanceof SelectableChannel)) {
            throw getRuntime().newErrnoENOPROTOOPTError();
        }
        SelectableChannel selectable = (SelectableChannel)channel;
        synchronized (selectable.blockingLock()) {
            boolean oldBlocking = selectable.isBlocking();
            try {
                selectable.configureBlocking(false);
                try {
                    doConnect(contextchanneladdr);
                } finally {
                    selectable.configureBlocking(oldBlocking);
                }
            } catch(ClosedChannelException e) {
                throw context.runtime.newErrnoECONNREFUSEDError();
            } catch(IOException e) {
                throw SocketUtils.sockerr(context.runtime"connect(2): name or service not known");
            }
        }
    }
    protected void doConnect(ThreadContext contextChannel channelSocketAddress addr) {
        Ruby runtime = context.runtime;
        try {
            if (channel instanceof SocketChannel) {
                SocketChannel socket = (SocketChannel)channel;
                boolean result;
                
                if (socket.isConnectionPending()) {
                    // connection initiated but not finished
                    result = socket.finishConnect();
                } else {
                    result = socket.connect(addr);
                }
                if(!result) {
                    if (runtime.is1_9()) {
                        throw runtime.newErrnoEINPROGRESSWritableError();
                    } else {
                        throw runtime.newErrnoEINPROGRESSError();
                    }
                }
            } else if (channel instanceof UnixSocketChannel) {
                ((UnixSocketChannel)channel).connect((UnixSocketAddress)addr);
            } else if (channel instanceof DatagramChannel) {
                ((DatagramChannel)channel).connect(addr);
            } else {
                throw runtime.newErrnoENOPROTOOPTError();
            }
        } catch(AlreadyConnectedException e) {
            throw runtime.newErrnoEISCONNError();
        } catch(ConnectionPendingException e) {
            if (runtime.is1_9()) {
                throw runtime.newErrnoEINPROGRESSWritableError();
            } else {
                throw runtime.newErrnoEINPROGRESSError();
            }
        } catch(UnknownHostException e) {
            throw SocketUtils.sockerr(runtime"connect(2): unknown host");
        } catch(SocketException e) {
            handleSocketException(runtime"connect"e);
        } catch(IOException e) {
            throw SocketUtils.sockerr(runtime"connect(2): name or service not known");
        } catch (IllegalArgumentException iae) {
            throw SocketUtils.sockerr(runtimeiae.getMessage());
        }
    }
    protected void doBind(ThreadContext contextChannel channelInetSocketAddress iaddr) {
        Ruby runtime = context.runtime;
        try {
            if (channel instanceof SocketChannel) {
                Socket socket = ((SocketChannel)channel).socket();
                socket.bind(iaddr);
            } else if (channel instanceof UnixSocketChannel) {
                // do nothing
            } else if (channel instanceof DatagramChannel) {
                DatagramSocket socket = ((DatagramChannel)channel).socket();
                socket.bind(iaddr);
            } else {
                throw runtime.newErrnoENOPROTOOPTError();
            }
        } catch(UnknownHostException e) {
            throw SocketUtils.sockerr(runtime"bind(2): unknown host");
        } catch(SocketException e) {
            handleSocketException(runtime"bind"e);
        } catch(IOException e) {
            throw SocketUtils.sockerr(runtime"bind(2): name or service not known");
        } catch (IllegalArgumentException iae) {
            throw SocketUtils.sockerr(runtimeiae.getMessage());
        }
    }
    protected void handleSocketException(Ruby runtimeString callerSocketException e) {
        String msg = formatMessage(e"bind");
        // This is ugly, but what can we do, Java provides the same exception type
        // for different situations, so we differentiate the errors
        // based on the exception's message.
        if (.matcher(msg).find()) {
            throw runtime.newErrnoEINVALError(msg);
        } else if (.matcher(msg).find()) {
            throw runtime.newErrnoEADDRNOTAVAILError(msg);
        } else if (.matcher(msg).find()) {
            throw runtime.newErrnoEACCESError(msg);
        } else {
            throw runtime.newErrnoEADDRINUSEError(msg);
        }
    }
    private static String formatMessage(Throwable eString defaultMsg) {
        String msg = e.getMessage();
        if (msg == null) {
            msg = defaultMsg;
        } else {
            msg = defaultMsg + " - " + msg;
        }
        return msg;
    }
    private SocketAddress addressForChannel(ThreadContext contextIRubyObject arg) {
        if (arg instanceof Addrinforeturn Sockaddr.addressFromArg(contextarg);
        switch () {
            case :
            case :
                return Sockaddr.addressFromSockaddr_un(contextarg);
            case :
            case :
            case :
                return Sockaddr.addressFromSockaddr_in(contextarg);
            default:
                throw context.runtime.newArgumentError("unsupported protocol family `" +  + "'");
        }
    }
    @Deprecated
    public static RuntimeException sockerr(Ruby runtimeString msg) {
        return new RaiseException(runtimeruntime.getClass("SocketError"), msgtrue);
    }
    private static final Pattern ALREADY_BOUND_PATTERN = Pattern.compile("[Aa]lready.*bound");
    private static final Pattern ADDR_NOT_AVAIL_PATTERN = Pattern.compile("assign.*address");
    private static final Pattern PERM_DENIED_PATTERN = Pattern.compile("[Pp]ermission.*denied");
    public static final int MSG_OOB = 0x1;
    public static final int MSG_PEEK = 0x2;
    public static final int MSG_DONTROUTE = 0x4;
    public static final int MSG_WAITALL = 0x100;
    protected AddressFamily soDomain;
    protected Sock soType;
    protected ProtocolFamily soProtocol;
    private static final String JRUBY_SERVER_SOCKET_ERROR =
            "use ServerSocket for servers (http://wiki.jruby.org/ServerSocket)";
}// RubySocket