public Encoding getEncodingFromString(String string) { if (string == null) return null; ByteList name = new ByteList(ByteList.plain(string)); checkAsciiEncodingName(name); SpecialEncoding special = SpecialEncoding.valueOf(name); if (special != null) { return special.toEncoding(runtime); } return findEncodingWithError(name); }
public void defaultVariablesUnmarshal(IRubyObject object) throws IOException { int count = unmarshalInt(); RubyClass cls = object.getMetaClass().getRealClass(); for (int i = 0; i < count; i++) { IRubyObject key = unmarshalObject(false); if (i == 0) { // first ivar provides encoding if (object instanceof EncodingCapable) { EncodingCapable strObj = (EncodingCapable) object; if (key.asJavaString().equals(MarshalStream.SYMBOL_ENCODING_SPECIAL)) { // special case for USASCII and UTF8 if (unmarshalObject().isTrue()) { strObj.setEncoding(UTF8Encoding.INSTANCE); } else { strObj.setEncoding(USASCIIEncoding.INSTANCE); } continue; } else if (key.asJavaString().equals("encoding")) { IRubyObject encodingNameObj = unmarshalObject(false); String encodingNameStr = encodingNameObj.asJavaString(); ByteList encodingName = new ByteList(ByteList.plain(encodingNameStr)); Entry entry = runtime.getEncodingService().findEncodingOrAliasEntry(encodingName); if (entry == null) { throw runtime.newArgumentError( "invalid encoding in marshaling stream: " + encodingName); } Encoding encoding = entry.getEncoding(); strObj.setEncoding(encoding); continue; } // else fall through as normal ivar } } String name = key.asJavaString(); IRubyObject value = unmarshalObject(); cls.getVariableAccessorForWrite(name).set(object, value); } }
/** 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(), BASICSOCKET_ALLOCATOR); rb_cBasicSocket.defineAnnotatedMethods(RubyBasicSocket.class); } private static ObjectAllocator BASICSOCKET_ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new RubyBasicSocket(runtime, klass); } }; public RubyBasicSocket(Ruby runtime, RubyClass type) { super(runtime, type); doNotReverseLookup = runtime.is1_9(); } @JRubyMethod(meta = true) public static IRubyObject for_fd(ThreadContext context, IRubyObject _klass, IRubyObject _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(runtime, klass); basicSocket.initSocket(runtime, descriptor); return basicSocket; } @JRubyMethod(name = "do_not_reverse_lookup", compat = CompatVersion.RUBY1_9) public IRubyObject do_not_reverse_lookup19(ThreadContext context) { return context.runtime.newBoolean(doNotReverseLookup); } @JRubyMethod(name = "do_not_reverse_lookup=", compat = CompatVersion.RUBY1_9) public IRubyObject set_do_not_reverse_lookup19(ThreadContext context, IRubyObject flag) { doNotReverseLookup = flag.isTrue(); return do_not_reverse_lookup19(context); } @JRubyMethod(meta = true) public static IRubyObject do_not_reverse_lookup(ThreadContext context, IRubyObject 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 context, IRubyObject recv, IRubyObject flag) { context.runtime.setDoNotReverseLookupEnabled(flag.isTrue()); return flag; } @JRubyMethod(name = "send") public IRubyObject send(ThreadContext context, IRubyObject _mesg, IRubyObject _flags) { // TODO: implement flags return syswrite(context, _mesg); } @JRubyMethod(name = "send") public IRubyObject send( ThreadContext context, IRubyObject _mesg, IRubyObject _flags, IRubyObject _to) { // TODO: implement flags and to return send(context, _mesg, _flags); } @Deprecated public IRubyObject recv(ThreadContext context, IRubyObject[] args) { switch (args.length) { case 1: return recv(context, args[0]); case 2: return recv(context, args[0], args[1]); default: Arity.raiseArgumentError(context.runtime, args, 1, 2); return null; // not reached } } @JRubyMethod public IRubyObject recv(ThreadContext context, IRubyObject _length) { Ruby runtime = context.runtime; ByteList bytes = doReceive(context, RubyNumeric.fix2int(_length)); if (bytes == null) return context.nil; return RubyString.newString(runtime, bytes); } @JRubyMethod public IRubyObject recv(ThreadContext context, IRubyObject _length, IRubyObject _flags) { // TODO: implement flags return recv(context, _length); } @JRubyMethod public IRubyObject recv_nonblock(ThreadContext context, IRubyObject _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(runtime, bytes); } @JRubyMethod public IRubyObject recv_nonblock(ThreadContext context, IRubyObject _length, IRubyObject _flags) { // TODO: implement flags return recv_nonblock(context, _length); } @JRubyMethod public IRubyObject getsockopt(ThreadContext context, IRubyObject _level, IRubyObject _opt) { Ruby runtime = context.runtime; SocketLevel level = levelFromArg(_level); SocketOption opt = optionFromArg(_opt); int value = 0; try { Channel channel = getOpenChannel(); switch (level) { case SOL_SOCKET: case SOL_IP: case SOL_TCP: case SOL_UDP: if (opt == SocketOption.__UNKNOWN_CONSTANT__) { throw runtime.newErrnoENOPROTOOPTError(); } value = SocketType.forChannel(channel).getSocketOption(channel, opt); if (runtime.is1_9()) { return new Option(runtime, ProtocolFamily.PF_INET, level, opt, value); } else { return number(runtime, SocketType.forChannel(channel).getSocketOption(channel, opt)); } default: throw runtime.newErrnoENOPROTOOPTError(); } } catch (BadDescriptorException e) { throw runtime.newErrnoEBADFError(); } catch (IOException e) { throw runtime.newErrnoENOPROTOOPTError(); } } @JRubyMethod public IRubyObject setsockopt( ThreadContext context, IRubyObject _level, IRubyObject _opt, IRubyObject 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 SOL_IP: case SOL_SOCKET: case SOL_TCP: case SOL_UDP: if (opt == SocketOption.SO_LINGER) { if (val instanceof RubyBoolean && !val.isTrue()) { socketType.setSoLinger(channel, false, 0); } else { int num = asNumber(val); if (num == -1) { socketType.setSoLinger(channel, false, 0); } else { socketType.setSoLinger(channel, true, num); } } } else { socketType.setSocketOption(channel, opt, asNumber(val)); } break; default: int intLevel = (int) _level.convertToInteger().getLongValue(); int intOpt = (int) _opt.convertToInteger().getLongValue(); if (IPPROTO_TCP.intValue() == intLevel && TCP_NODELAY.intValue() == intOpt) { socketType.setTcpNoDelay(channel, asBoolean(val)); } else if (IPPROTO_IP.intValue() == intLevel) { if (MulticastStateManager.IP_ADD_MEMBERSHIP == 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 = CompatVersion.RUBY1_9, notImplemented = true) public IRubyObject getpeereid(ThreadContext context) { throw context.runtime.newNotImplementedError("getpeereid not implemented"); } @JRubyMethod(compat = CompatVersion.RUBY1_9) public IRubyObject local_address(ThreadContext context) { try { InetSocketAddress address = getSocketAddress(); if (address == null) { return context.nil; } else { return new Addrinfo( context.runtime, context.runtime.getClass("Addrinfo"), address.getAddress(), address.getPort(), SocketType.forChannel(getChannel())); } } catch (BadDescriptorException bde) { throw context.runtime.newErrnoEBADFError("address unavailable"); } } @JRubyMethod(compat = CompatVersion.RUBY1_9) public IRubyObject remote_address(ThreadContext context) { try { InetSocketAddress address = getRemoteSocket(); if (address == null) { return context.nil; } else { return new Addrinfo( context.runtime, context.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 context, IRubyObject[] args) { int how = 2; if (args.length > 0) { String howString = null; if (args[0] instanceof RubyString) { howString = ((RubyString) args[0]).asJavaString(); } else if (args[0] instanceof RubySymbol) { howString = ((RubySymbol) args[0]).asJavaString(); } if (howString != null) { if (howString.equals("RD") || howString.equals("SHUT_RD")) { how = 0; } else if (howString.equals("WR") || howString.equals("SHUT_WR")) { how = 1; } else if (howString.equals("RDWR") || howString.equals("SHUT_RDWR")) { how = 2; } } else { how = RubyNumeric.fix2int(args[0]); } } try { return shutdownInternal(context, how); } catch (BadDescriptorException e) { throw context.runtime.newErrnoEBADFError(); } } @Override @JRubyMethod public IRubyObject close_write(ThreadContext context) { Ruby runtime = context.runtime; if (!openFile.isWritable()) { return runtime.getNil(); } if (openFile.getPipeStream() == null && openFile.isReadable()) { throw runtime.newIOError("closing non-duplex IO for writing"); } if (!openFile.isReadable()) { close(); } else { // shutdown write try { shutdownInternal(context, 1); } catch (BadDescriptorException e) { throw runtime.newErrnoEBADFError(); } } return context.nil; } @Override @JRubyMethod public IRubyObject close_read(ThreadContext context) { Ruby runtime = context.runtime; if (!openFile.isOpen()) { throw context.runtime.newIOError("not opened for reading"); } if (!openFile.isWritable()) { close(); } else { // shutdown read try { shutdownInternal(context, 0); } catch (BadDescriptorException e) { throw runtime.newErrnoEBADFError(); } } return context.nil; } @JRubyMethod(rest = true, notImplemented = true, compat = CompatVersion.RUBY1_9) public IRubyObject sendmsg(ThreadContext context, IRubyObject[] args) { throw context.runtime.newNotImplementedError("sendmsg is not implemented"); } @JRubyMethod(rest = true, notImplemented = true, compat = CompatVersion.RUBY1_9) public IRubyObject sendmsg_nonblock(ThreadContext context, IRubyObject[] args) { throw context.runtime.newNotImplementedError("sendmsg_nonblock is not implemented"); } @JRubyMethod(rest = true, notImplemented = true, compat = CompatVersion.RUBY1_9) public IRubyObject readmsg(ThreadContext context, IRubyObject[] args) { throw context.runtime.newNotImplementedError("readmsg is not implemented"); } @JRubyMethod(rest = true, notImplemented = true, compat = CompatVersion.RUBY1_9) public IRubyObject readmsg_nonblock(ThreadContext context, IRubyObject[] args) { throw context.runtime.newNotImplementedError("readmsg_nonblock is not implemented"); } private ByteList doReceive(ThreadContext context, int length) { Ruby runtime = context.runtime; ByteBuffer buf = ByteBuffer.allocate(length); try { context.getThread().beforeBlockingCall(); int read = openFile.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 context, int 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(context, length); } finally { selectable.configureBlocking(oldBlocking); } } catch (IOException e) { throw runtime.newIOErrorFromException(e); } } } private void joinMulticastGroup(IRubyObject val) throws IOException, BadDescriptorException { Channel socketChannel = getOpenChannel(); if (socketChannel instanceof DatagramChannel) { if (multicastStateManager == null) { multicastStateManager = new MulticastStateManager(); } if (val instanceof RubyString) { byte[] ipaddr_buf = val.convertToString().getBytes(); multicastStateManager.addMembership(ipaddr_buf); } } } protected InetSocketAddress getSocketAddress() throws BadDescriptorException { Channel channel = getOpenChannel(); return (InetSocketAddress) SocketType.forChannel(channel).getLocalSocketAddress(channel); } protected InetSocketAddress getRemoteSocket() throws BadDescriptorException { Channel channel = getOpenChannel(); return (InetSocketAddress) SocketType.forChannel(channel).getRemoteSocketAddress(channel); } protected Sock getDefaultSocketType() { return Sock.SOCK_STREAM; } protected IRubyObject getSocknameCommon(ThreadContext context, String 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(context, sock); } } catch (BadDescriptorException e) { throw context.runtime.newErrnoEBADFError(); } } private IRubyObject shutdownInternal(ThreadContext context, int how) throws 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 (openFile.getPipeStream() != null) { openFile.setMainStream(openFile.getPipeStream()); openFile.setPipeStream(null); } openFile.setMode(openFile.getMode() & ~OpenFile.READABLE); return RubyFixnum.zero(runtime); case 1: channel = getOpenChannel(); try { SocketType.forChannel(channel).shutdownOutput(channel); } catch (IOException e) { throw runtime.newIOError(e.getMessage()); } openFile.setPipeStream(null); openFile.setMode(openFile.getMode() & ~OpenFile.WRITABLE); return RubyFixnum.zero(runtime); case 2: shutdownInternal(context, 0); shutdownInternal(context, 1); return RubyFixnum.zero(runtime); default: throw runtime.newArgumentError("`how' should be either :SHUT_RD, :SHUT_WR, :SHUT_RDWR"); } } public boolean doNotReverseLookup(ThreadContext context) { return context.runtime.isDoNotReverseLookupEnabled() || doNotReverseLookup; } protected void initSocket(Ruby runtime, ChannelDescriptor descriptor) { // continue with normal initialization MakeOpenFile(); try { openFile.setMainStream( ChannelStream.fdopen(runtime, descriptor, newModeFlags(runtime, ModeFlags.RDONLY))); openFile.setPipeStream( ChannelStream.fdopen(runtime, descriptor, newModeFlags(runtime, ModeFlags.WRONLY))); openFile.getPipeStream().setSync(true); } catch (org.jruby.util.io.InvalidValueException ex) { throw runtime.newErrnoEINVALError(); } openFile.setMode(OpenFile.READWRITE | OpenFile.SYNC); // see rsock_init_sock in MRI; sockets are initialized to binary setAscii8bitBinmode(); } private Channel getOpenChannel() throws BadDescriptorException { return getOpenFileChecked().getMainStreamSafe().getDescriptor().getChannel(); } 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, FORMAT_SMALL_I).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 runtime, int 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 context, InetSocketAddress addr, boolean 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 lev, IRubyObject optname) { return getsockopt(getRuntime().getCurrentContext(), lev, optname); } @Deprecated public IRubyObject setsockopt(IRubyObject lev, IRubyObject optname, IRubyObject val) { return setsockopt(getRuntime().getCurrentContext(), lev, optname, val); } @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 recv, IRubyObject flag) { return set_do_not_reverse_lookup(recv.getRuntime().getCurrentContext(), recv, flag); } 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
/** @author <a href="mailto:[email protected]">Ola Bini</a> */ @JRubyClass(name = "BasicSocket", parent = "IO") public class RubyBasicSocket extends RubyIO { private static final ByteList FORMAT_SMALL_I = new ByteList(ByteList.plain("i")); 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); } // By default we always reverse lookup unless do_not_reverse_lookup set. private boolean doNotReverseLookup = false; public RubyBasicSocket(Ruby runtime, RubyClass type) { super(runtime, type); } protected void initSocket(Ruby runtime, ChannelDescriptor 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.isWritable()) { return context.getRuntime().getNil(); } if (openFile.getPipeStream() == null && openFile.isReadable()) { throw context.getRuntime().newIOError("closing non-duplex IO for writing"); } if (!openFile.isReadable()) { close(); } else { // shutdown write shutdownInternal(context, 1); } 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.isOpen()) { throw context.getRuntime().newIOError("not opened for reading"); } if (!openFile.isWritable()) { close(); } else { // shutdown read shutdownInternal(context, 0); } 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 { context.getThread().beforeBlockingCall(); 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()); } finally { context.getThread().afterBlockingCall(); } } 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 setTcpNoDelay(IRubyObject val) throws IOException { Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel(); if (socketChannel instanceof SocketChannel) { asSocket().setTcpNoDelay(asBoolean(val)); } } private void setReuseAddr(IRubyObject val) throws IOException { Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel(); if (socketChannel instanceof ServerSocketChannel) { asServerSocket().setReuseAddress(asBoolean(val)); } else if (socketChannel instanceof SocketChannel) { asSocket().setReuseAddress(asBoolean(val)); } else if (socketChannel instanceof DatagramChannel) { asDatagramSocket().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 { try { Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel(); if (socketChannel instanceof SocketChannel) { asSocket().setSendBufferSize(asNumber(val)); } else if (socketChannel instanceof DatagramChannel) { asDatagramSocket().setSendBufferSize(asNumber(val)); } } catch (IllegalArgumentException iae) { throw getRuntime().newErrnoEINVALError(iae.getMessage()); } } 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 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, FORMAT_SMALL_I).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 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(); int linger = 0; if (socketChannel instanceof SocketChannel) { linger = asSocket().getSoLinger(); if (linger < 0) { linger = 0; } } return number(runtime, linger); } 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(); boolean reuse = false; if (socketChannel instanceof ServerSocketChannel) { reuse = asServerSocket().getReuseAddress(); } else if (socketChannel instanceof SocketChannel) { reuse = asSocket().getReuseAddress(); } else if (socketChannel instanceof DatagramChannel) { reuse = asDatagramSocket().getReuseAddress(); } return trueFalse(runtime, reuse); } 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 int getChannelSoType(Channel channel) { if (channel instanceof SocketChannel || channel instanceof ServerSocketChannel) { return SOCK_STREAM.value(); } else if (channel instanceof DatagramChannel) { return SOCK_DGRAM.value(); } else { return getSoTypeDefault(); } } private IRubyObject getSoType(Ruby runtime) throws IOException { Channel socketChannel = openFile.getMainStream().getDescriptor().getChannel(); return number(runtime, getChannelSoType(socketChannel)); } private IRubyObject trueFalse(Ruby runtime, boolean val) { return number(runtime, val ? 1 : 0); } private static IRubyObject number(Ruby runtime, int s) { RubyArray array = runtime.newArray(runtime.newFixnum(s)); return Pack.pack(runtime, array, FORMAT_SMALL_I); } @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 (SocketLevel.valueOf(level)) { case SOL_IP: case SOL_SOCKET: case SOL_TCP: case SOL_UDP: switch (SocketOption.valueOf(opt)) { case SO_BROADCAST: return getBroadcast(runtime); case SO_KEEPALIVE: return getKeepAlive(runtime); case SO_LINGER: return getLinger(runtime); case SO_OOBINLINE: return getOOBInline(runtime); case SO_RCVBUF: return getRcvBuf(runtime); case SO_REUSEADDR: return getReuseAddr(runtime); case SO_SNDBUF: return getSndBuf(runtime); case SO_RCVTIMEO: case SO_SNDTIMEO: return getTimeout(runtime); case SO_TYPE: return getSoType(runtime); // Can't support the rest with Java case SO_RCVLOWAT: return number(runtime, 1); case SO_SNDLOWAT: return number(runtime, 2048); case SO_DEBUG: case SO_ERROR: case SO_DONTROUTE: case 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 (SocketLevel.valueOf(level)) { case SOL_IP: case SOL_SOCKET: case SOL_TCP: case SOL_UDP: switch (SocketOption.valueOf(opt)) { case SO_BROADCAST: setBroadcast(val); break; case SO_KEEPALIVE: setKeepAlive(val); break; case SO_LINGER: setLinger(val); break; case SO_OOBINLINE: setOOBInline(val); break; case SO_RCVBUF: setRcvBuf(val); break; case SO_REUSEADDR: setReuseAddr(val); break; case SO_SNDBUF: setSndBuf(val); break; case SO_RCVTIMEO: case SO_SNDTIMEO: setTimeout(val); break; // Can't support the rest with Java case SO_TYPE: case SO_RCVLOWAT: case SO_SNDLOWAT: case SO_DEBUG: case SO_ERROR: case SO_DONTROUTE: case SO_TIMESTAMP: break; default: if (IPPROTO_TCP.value() == level && TCP_NODELAY.value() == opt) { setTcpNoDelay(val); } else { throw context.getRuntime().newErrnoENOPROTOOPTError(); } } break; default: if (IPPROTO_TCP.value() == level && TCP_NODELAY.value() == opt) { setTcpNoDelay(val); } else { 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) { return RubySocket.pack_sockaddr_in(context, null, 0, "0.0.0.0"); } 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]); } return shutdownInternal(context, how); } private IRubyObject shutdownInternal(ThreadContext context, int how) { Channel socketChannel; switch (how) { case 0: socketChannel = openFile.getMainStream().getDescriptor().getChannel(); try { if (socketChannel instanceof SocketChannel || socketChannel instanceof DatagramChannel) { asSocket().shutdownInput(); } else if (socketChannel instanceof Shutdownable) { ((Shutdownable) socketChannel).shutdownInput(); } } catch (IOException e) { throw context.getRuntime().newIOError(e.getMessage()); } if (openFile.getPipeStream() != null) { openFile.setMainStream(openFile.getPipeStream()); openFile.setPipeStream(null); } openFile.setMode(openFile.getMode() & ~OpenFile.READABLE); return RubyFixnum.zero(context.getRuntime()); case 1: socketChannel = openFile.getMainStream().getDescriptor().getChannel(); try { if (socketChannel instanceof SocketChannel || socketChannel instanceof DatagramChannel) { asSocket().shutdownOutput(); } else if (socketChannel instanceof Shutdownable) { ((Shutdownable) socketChannel).shutdownOutput(); } } catch (IOException e) { throw context.getRuntime().newIOError(e.getMessage()); } openFile.setPipeStream(null); openFile.setMode(openFile.getMode() & ~OpenFile.WRITABLE); return RubyFixnum.zero(context.getRuntime()); case 2: shutdownInternal(context, 0); shutdownInternal(context, 1); return RubyFixnum.zero(context.getRuntime()); default: throw context.getRuntime().newArgumentError("`how' should be either 0, 1, 2"); } } protected boolean doNotReverseLookup(ThreadContext context) { return context.getRuntime().isDoNotReverseLookupEnabled() || doNotReverseLookup; } @JRubyMethod(compat = CompatVersion.RUBY1_9) public IRubyObject do_not_reverse_lookup19(ThreadContext context) { return context.getRuntime().newBoolean(doNotReverseLookup); } @JRubyMethod(name = "do_not_reverse_lookup=", compat = CompatVersion.RUBY1_9) public IRubyObject set_do_not_reverse_lookup19(ThreadContext context, IRubyObject flag) { doNotReverseLookup = flag.isTrue(); return do_not_reverse_lookup19(context); } @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