PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/org/jruby/ext/socket/RubyTCPSocket.java

http://github.com/jruby/jruby
Java | 186 lines | 133 code | 21 blank | 32 comment | 12 complexity | b26263b43b9c6532bc7c7999e7d54dff MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, GPL-2.0, JSON, LGPL-2.1
  1. /*
  2. ***** BEGIN LICENSE BLOCK *****
  3. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Common Public
  6. * License Version 1.0 (the "License"); you may not use this file
  7. * except in compliance with the License. You may obtain a copy of
  8. * the License at http://www.eclipse.org/legal/cpl-v10.html
  9. *
  10. * Software distributed under the License is distributed on an "AS
  11. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12. * implied. See the License for the specific language governing
  13. * rights and limitations under the License.
  14. *
  15. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  16. * Copyright (C) 2007 Thomas E Enebo <enebo@acm.org>
  17. *
  18. * Alternatively, the contents of this file may be used under the terms of
  19. * either of the GNU General Public License Version 2 or later (the "GPL"),
  20. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  21. * in which case the provisions of the GPL or the LGPL are applicable instead
  22. * of those above. If you wish to allow use of your version of this file only
  23. * under the terms of either the GPL or the LGPL, and not to allow others to
  24. * use your version of this file under the terms of the CPL, indicate your
  25. * decision by deleting the provisions above and replace them with the notice
  26. * and other provisions required by the GPL or the LGPL. If you do not delete
  27. * the provisions above, a recipient may use your version of this file under
  28. * the terms of any one of the CPL, the GPL or the LGPL.
  29. ***** END LICENSE BLOCK *****/
  30. package org.jruby.ext.socket;
  31. import static jnr.constants.platform.AddressFamily.*;
  32. import java.io.IOException;
  33. import java.net.BindException;
  34. import java.net.ConnectException;
  35. import java.net.Inet4Address;
  36. import java.net.Inet6Address;
  37. import java.net.InetAddress;
  38. import java.net.InetSocketAddress;
  39. import java.net.NoRouteToHostException;
  40. import java.net.Socket;
  41. import java.net.UnknownHostException;
  42. import java.nio.channels.ClosedChannelException;
  43. import java.nio.channels.SelectionKey;
  44. import java.nio.channels.SocketChannel;
  45. import org.jruby.Ruby;
  46. import org.jruby.RubyClass;
  47. import org.jruby.RubyNumeric;
  48. import org.jruby.RubyString;
  49. import org.jruby.anno.JRubyMethod;
  50. import org.jruby.runtime.Block;
  51. import org.jruby.runtime.ObjectAllocator;
  52. import org.jruby.runtime.ThreadContext;
  53. import org.jruby.runtime.Visibility;
  54. import org.jruby.runtime.builtin.IRubyObject;
  55. import org.jruby.util.io.ModeFlags;
  56. import org.jruby.util.io.ChannelDescriptor;
  57. public class RubyTCPSocket extends RubyIPSocket {
  58. static void createTCPSocket(Ruby runtime) {
  59. RubyClass rb_cTCPSocket = runtime.defineClass("TCPSocket", runtime.getClass("IPSocket"), TCPSOCKET_ALLOCATOR);
  60. rb_cTCPSocket.includeModule(runtime.getClass("Socket").getConstant("Constants"));
  61. rb_cTCPSocket.defineAnnotatedMethods(RubyTCPSocket.class);
  62. runtime.getObject().setConstant("TCPsocket",rb_cTCPSocket);
  63. }
  64. private static ObjectAllocator TCPSOCKET_ALLOCATOR = new ObjectAllocator() {
  65. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  66. return new RubyTCPSocket(runtime, klass);
  67. }
  68. };
  69. public RubyTCPSocket(Ruby runtime, RubyClass type) {
  70. super(runtime, type);
  71. }
  72. private int getPortFrom(Ruby runtime, IRubyObject arg) {
  73. if (arg instanceof RubyString) {
  74. jnr.netdb.Service service = jnr.netdb.Service.getServiceByName(arg.asJavaString(), "tcp");
  75. return service != null ?
  76. service.getPort() : RubyNumeric.fix2int(RubyNumeric.str2inum(runtime, (RubyString) arg, 0, true));
  77. }
  78. return RubyNumeric.fix2int(arg);
  79. }
  80. @JRubyMethod(required = 2, optional = 2, visibility = Visibility.PRIVATE, backtrace = true)
  81. public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
  82. Ruby runtime = context.runtime;
  83. String remoteHost = args[0].isNil()? "localhost" : args[0].convertToString().toString();
  84. int remotePort = getPortFrom(context.getRuntime(), args[1]);
  85. String localHost = args.length >= 3 && !args[2].isNil() ? args[2].convertToString().toString() : null;
  86. int localPort = args.length == 4 && !args[3].isNil() ? getPortFrom(context.getRuntime(), args[3]) : 0;
  87. try {
  88. // This is a bit convoluted because (1) SocketChannel.bind is only in jdk 7 and
  89. // (2) Socket.getChannel() seems to return null in some cases
  90. final SocketChannel channel = SocketChannel.open();
  91. final Socket socket = channel.socket();
  92. if (localHost != null) {
  93. socket.bind( new InetSocketAddress(InetAddress.getByName(localHost), localPort) );
  94. }
  95. try {
  96. channel.configureBlocking(false);
  97. channel.connect( new InetSocketAddress(InetAddress.getByName(remoteHost), remotePort) );
  98. context.getThread().select(channel, this, SelectionKey.OP_CONNECT);
  99. channel.finishConnect();
  100. // only try to set blocking back if we succeeded to finish connecting
  101. channel.configureBlocking(true);
  102. initSocket(runtime, new ChannelDescriptor(channel, newModeFlags(runtime, ModeFlags.RDWR)));
  103. } catch (NoRouteToHostException nrthe) {
  104. channel.close();
  105. throw runtime.newErrnoEHOSTUNREACHError("SocketChannel.connect");
  106. } catch(ConnectException e) {
  107. channel.close();
  108. throw runtime.newErrnoECONNREFUSEDError();
  109. } catch(UnknownHostException e) {
  110. channel.close();
  111. throw sockerr(runtime, "initialize: name or service not known");
  112. }
  113. } catch (ClosedChannelException cce) {
  114. throw runtime.newErrnoECONNREFUSEDError();
  115. } catch(BindException e) {
  116. throw runtime.newErrnoEADDRFromBindException(e);
  117. } catch(IOException e) {
  118. throw sockerr(runtime, e.getLocalizedMessage());
  119. } catch (IllegalArgumentException iae) {
  120. throw sockerr(runtime, iae.getMessage());
  121. }
  122. return this;
  123. }
  124. @Deprecated
  125. public static IRubyObject open(IRubyObject recv, IRubyObject[] args, Block block) {
  126. return open(recv.getRuntime().getCurrentContext(), recv, args, block);
  127. }
  128. @JRubyMethod(rest = true, meta = true)
  129. public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  130. RubyTCPSocket sock = (RubyTCPSocket)recv.callMethod(context,"new",args);
  131. if (!block.isGiven()) return sock;
  132. try {
  133. return block.yield(context, sock);
  134. } finally {
  135. if (sock.openFile.isOpen()) sock.close();
  136. }
  137. }
  138. @Deprecated
  139. public static IRubyObject gethostbyname(IRubyObject recv, IRubyObject hostname) {
  140. return gethostbyname(recv.getRuntime().getCurrentContext(), recv, hostname);
  141. }
  142. @JRubyMethod(meta = true)
  143. public static IRubyObject gethostbyname(ThreadContext context, IRubyObject recv, IRubyObject hostname) {
  144. try {
  145. IRubyObject[] ret = new IRubyObject[4];
  146. Ruby r = context.getRuntime();
  147. InetAddress addr;
  148. String hostString = hostname.convertToString().toString();
  149. addr = InetAddress.getByName(hostString);
  150. ret[0] = r.newString(do_not_reverse_lookup(recv).isTrue() ? addr.getHostAddress() : addr.getCanonicalHostName());
  151. ret[1] = r.newArray();
  152. ret[3] = r.newString(addr.getHostAddress());
  153. if (addr instanceof Inet4Address) {
  154. Inet4Address addr4 = (Inet4Address)addr;
  155. ret[2] = r.newFixnum(AF_INET); //AF_INET
  156. } else if (addr instanceof Inet6Address) {
  157. Inet6Address addr6 = (Inet6Address)addr;
  158. ret[2] = r.newFixnum(AF_INET6); //AF_INET
  159. }
  160. return r.newArrayNoCopy(ret);
  161. } catch(UnknownHostException e) {
  162. throw sockerr(context.getRuntime(), "gethostbyname: name or service not known");
  163. }
  164. }
  165. }