PageRenderTime 68ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/jruby-1.7.3/src/org/jruby/ext/socket/RubyUDPSocket.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 536 lines | 354 code | 124 blank | 58 comment | 44 complexity | d3d4010cb7f395e42e6611a5016dfe52 MD5 | raw file
  1. /***** BEGIN LICENSE BLOCK *****
  2. * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Common Public
  5. * License Version 1.0 (the "License"); you may not use this file
  6. * except in compliance with the License. You may obtain a copy of
  7. * the License at http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Software distributed under the License is distributed on an "AS
  10. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11. * implied. See the License for the specific language governing
  12. * rights and limitations under the License.
  13. *
  14. * Copyright (C) 2007 Damian Steer <pldms@mac.com>
  15. *
  16. * Alternatively, the contents of this file may be used under the terms of
  17. * either of the GNU General Public License Version 2 or later (the "GPL"),
  18. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  19. * in which case the provisions of the GPL or the LGPL are applicable instead
  20. * of those above. If you wish to allow use of your version of this file only
  21. * under the terms of either the GPL or the LGPL, and not to allow others to
  22. * use your version of this file under the terms of the EPL, indicate your
  23. * decision by deleting the provisions above and replace them with the notice
  24. * and other provisions required by the GPL or the LGPL. If you do not delete
  25. * the provisions above, a recipient may use your version of this file under
  26. * the terms of any one of the EPL, the GPL or the LGPL.
  27. ***** END LICENSE BLOCK *****/
  28. package org.jruby.ext.socket;
  29. import java.io.IOException;
  30. import java.net.ConnectException;
  31. import java.net.InetAddress;
  32. import java.net.InetSocketAddress;
  33. import java.net.PortUnreachableException;
  34. import java.net.SocketException;
  35. import java.net.MulticastSocket;
  36. import java.net.UnknownHostException;
  37. import java.net.DatagramPacket;
  38. import java.nio.ByteBuffer;
  39. import java.nio.channels.Channel;
  40. import java.nio.channels.DatagramChannel;
  41. import java.nio.channels.IllegalBlockingModeException;
  42. import java.nio.channels.NotYetConnectedException;
  43. import jnr.netdb.Service;
  44. import org.jruby.Ruby;
  45. import org.jruby.RubyClass;
  46. import org.jruby.RubyFixnum;
  47. import org.jruby.RubyModule;
  48. import org.jruby.RubyNumeric;
  49. import org.jruby.RubyString;
  50. import org.jruby.anno.JRubyClass;
  51. import org.jruby.anno.JRubyMethod;
  52. import org.jruby.runtime.Arity;
  53. import org.jruby.runtime.Block;
  54. import org.jruby.runtime.ObjectAllocator;
  55. import org.jruby.runtime.ThreadContext;
  56. import org.jruby.runtime.Visibility;
  57. import org.jruby.runtime.builtin.IRubyObject;
  58. import org.jruby.util.ByteList;
  59. import org.jruby.util.io.ModeFlags;
  60. import org.jruby.util.io.ChannelDescriptor;
  61. /**
  62. * @author <a href="mailto:pldms@mac.com">Damian Steer</a>
  63. */
  64. @JRubyClass(name="UDPSocket", parent="IPSocket")
  65. public class RubyUDPSocket extends RubyIPSocket {
  66. static void createUDPSocket(Ruby runtime) {
  67. RubyClass rb_cUDPSocket = runtime.defineClass("UDPSocket", runtime.getClass("IPSocket"), UDPSOCKET_ALLOCATOR);
  68. rb_cUDPSocket.includeModule(runtime.getClass("Socket").getConstant("Constants"));
  69. rb_cUDPSocket.defineAnnotatedMethods(RubyUDPSocket.class);
  70. runtime.getObject().setConstant("UDPsocket", rb_cUDPSocket);
  71. }
  72. private static ObjectAllocator UDPSOCKET_ALLOCATOR = new ObjectAllocator() {
  73. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  74. return new RubyUDPSocket(runtime, klass);
  75. }
  76. };
  77. public RubyUDPSocket(Ruby runtime, RubyClass type) {
  78. super(runtime, type);
  79. }
  80. @JRubyMethod(visibility = Visibility.PRIVATE)
  81. public IRubyObject initialize(ThreadContext context) {
  82. Ruby runtime = context.runtime;
  83. try {
  84. DatagramChannel channel = DatagramChannel.open();
  85. initSocket(runtime, new ChannelDescriptor(channel, newModeFlags(runtime, ModeFlags.RDWR)));
  86. } catch (ConnectException e) {
  87. throw runtime.newErrnoECONNREFUSEDError();
  88. } catch (UnknownHostException e) {
  89. throw SocketUtils.sockerr(runtime, "initialize: name or service not known");
  90. } catch (IOException e) {
  91. throw SocketUtils.sockerr(runtime, "initialize: name or service not known");
  92. }
  93. return this;
  94. }
  95. @JRubyMethod(visibility = Visibility.PRIVATE)
  96. public IRubyObject initialize(ThreadContext context, IRubyObject protocol) {
  97. // we basically ignore protocol. let someone report it...
  98. return initialize(context);
  99. }
  100. @JRubyMethod
  101. public IRubyObject bind(ThreadContext context, IRubyObject host, IRubyObject _port) {
  102. Ruby runtime = context.runtime;
  103. InetSocketAddress addr = null;
  104. try {
  105. Channel channel = getChannel();
  106. int port = SocketUtils.portToInt(_port);
  107. if (host.isNil()
  108. || ((host instanceof RubyString)
  109. && ((RubyString) host).isEmpty())) {
  110. // host is nil or the empty string, bind to INADDR_ANY
  111. addr = new InetSocketAddress(port);
  112. } else if (host instanceof RubyFixnum) {
  113. // passing in something like INADDR_ANY
  114. int intAddr = RubyNumeric.fix2int(host);
  115. RubyModule socketMod = runtime.getModule("Socket");
  116. if (intAddr == RubyNumeric.fix2int(socketMod.getConstant("INADDR_ANY"))) {
  117. addr = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), port);
  118. }
  119. } else {
  120. // passing in something like INADDR_ANY
  121. addr = new InetSocketAddress(InetAddress.getByName(host.convertToString().toString()), port);
  122. }
  123. if (multicastStateManager == null) {
  124. ((DatagramChannel) channel).socket().bind(addr);
  125. } else {
  126. multicastStateManager.rebindToPort(port);
  127. }
  128. return RubyFixnum.zero(runtime);
  129. } catch (UnknownHostException e) {
  130. throw SocketUtils.sockerr(runtime, "bind: name or service not known");
  131. } catch (SocketException e) {
  132. throw SocketUtils.sockerr(runtime, "bind: name or service not known");
  133. } catch (IOException e) {
  134. throw SocketUtils.sockerr(runtime, "bind: name or service not known");
  135. } catch (Error e) {
  136. // Workaround for a bug in Sun's JDK 1.5.x, see
  137. // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6303753
  138. if (e.getCause() instanceof SocketException) {
  139. throw SocketUtils.sockerr(runtime, "bind: name or service not known");
  140. } else {
  141. throw e;
  142. }
  143. }
  144. }
  145. @JRubyMethod
  146. public IRubyObject connect(ThreadContext context, IRubyObject host, IRubyObject port) {
  147. Ruby runtime = context.runtime;
  148. try {
  149. InetSocketAddress addr = new InetSocketAddress(InetAddress.getByName(host.convertToString().toString()), SocketUtils.portToInt(port));
  150. ((DatagramChannel) this.getChannel()).connect(addr);
  151. return RubyFixnum.zero(runtime);
  152. } catch (UnknownHostException e) {
  153. throw SocketUtils.sockerr(runtime, "connect: name or service not known");
  154. } catch (IOException e) {
  155. throw SocketUtils.sockerr(runtime, "connect: name or service not known");
  156. }
  157. }
  158. @JRubyMethod
  159. public IRubyObject recvfrom_nonblock(ThreadContext context, IRubyObject _length) {
  160. Ruby runtime = context.runtime;
  161. try {
  162. int length = RubyNumeric.fix2int(_length);
  163. ReceiveTuple tuple = doReceiveNonblockTuple(runtime, length);
  164. IRubyObject addressArray = addrFor(context, tuple.sender, false);
  165. return runtime.newArray(tuple.result, addressArray);
  166. } catch (UnknownHostException e) {
  167. throw SocketUtils.sockerr(runtime, "recvfrom: name or service not known");
  168. } catch (PortUnreachableException e) {
  169. throw runtime.newErrnoECONNREFUSEDError();
  170. } catch (IOException e) {
  171. throw SocketUtils.sockerr(runtime, "recvfrom: name or service not known");
  172. }
  173. }
  174. @JRubyMethod
  175. public IRubyObject recvfrom_nonblock(ThreadContext context, IRubyObject _length, IRubyObject _flags) {
  176. // TODO: handle flags
  177. return recvfrom_nonblock(context, _length);
  178. }
  179. @JRubyMethod
  180. public IRubyObject send(ThreadContext context, IRubyObject _mesg, IRubyObject _flags) {
  181. // TODO: implement flags
  182. Ruby runtime = context.runtime;
  183. try {
  184. int written;
  185. RubyString data = _mesg.convertToString();
  186. ByteBuffer buf = ByteBuffer.wrap(data.getBytes());
  187. written = ((DatagramChannel) this.getChannel()).write(buf);
  188. return runtime.newFixnum(written);
  189. } catch (NotYetConnectedException nyce) {
  190. throw runtime.newErrnoEDESTADDRREQError("send(2)");
  191. } catch (UnknownHostException e) {
  192. throw SocketUtils.sockerr(runtime, "send: name or service not known");
  193. } catch (IOException e) {
  194. throw SocketUtils.sockerr(runtime, "send: name or service not known");
  195. }
  196. }
  197. @JRubyMethod
  198. public IRubyObject send(ThreadContext context, IRubyObject _mesg, IRubyObject _flags, IRubyObject _to) {
  199. return send(context, _mesg, _flags);
  200. }
  201. @JRubyMethod(required = 2, optional = 2)
  202. public IRubyObject send(ThreadContext context, IRubyObject[] args) {
  203. // TODO: implement flags
  204. Ruby runtime = context.runtime;
  205. IRubyObject _mesg = args[0];
  206. IRubyObject _flags = args[1];
  207. try {
  208. int written;
  209. if (args.length == 2 || args.length == 3) {
  210. return send(context, _mesg, _flags);
  211. }
  212. IRubyObject _host = args[2];
  213. IRubyObject _port = args[3];
  214. RubyString nameStr = _host.convertToString();
  215. RubyString data = _mesg.convertToString();
  216. ByteBuffer buf = ByteBuffer.wrap(data.getBytes());
  217. byte[] buf2 = data.getBytes();
  218. DatagramPacket sendDP = null;
  219. int port;
  220. if (_port instanceof RubyString) {
  221. Service service = Service.getServiceByName(_port.asJavaString(), "udp");
  222. if (service != null) {
  223. port = service.getPort();
  224. } else {
  225. port = (int)_port.convertToInteger("to_i").getLongValue();
  226. }
  227. } else {
  228. port = (int)_port.convertToInteger().getLongValue();
  229. }
  230. InetAddress address = SocketUtils.getRubyInetAddress(nameStr.getByteList());
  231. InetSocketAddress addr = new InetSocketAddress(address, port);
  232. if (this.multicastStateManager == null) {
  233. written = ((DatagramChannel) this.getChannel()).send(buf, addr);
  234. } else {
  235. sendDP = new DatagramPacket(buf2, buf2.length, address, port);
  236. multicastStateManager.rebindToPort(port);
  237. MulticastSocket ms = this.multicastStateManager.getMulticastSocket();
  238. ms.send(sendDP);
  239. written = sendDP.getLength();
  240. }
  241. return runtime.newFixnum(written);
  242. } catch (UnknownHostException e) {
  243. throw SocketUtils.sockerr(runtime, "send: name or service not known");
  244. } catch (IOException e) {
  245. throw SocketUtils.sockerr(runtime, "send: name or service not known");
  246. }
  247. }
  248. @JRubyMethod(rest = true, meta = true)
  249. public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  250. RubyUDPSocket sock = (RubyUDPSocket) recv.callMethod(context, "new", args);
  251. if (!block.isGiven()) {
  252. return sock;
  253. }
  254. try {
  255. return block.yield(context, sock);
  256. } finally {
  257. if (sock.openFile.isOpen()) {
  258. sock.close();
  259. }
  260. }
  261. }
  262. /**
  263. * Overrides IPSocket#recvfrom
  264. */
  265. @Override
  266. public IRubyObject recvfrom(ThreadContext context, IRubyObject _length) {
  267. Ruby runtime = context.runtime;
  268. try {
  269. int length = RubyNumeric.fix2int(_length);
  270. ReceiveTuple tuple = doReceiveTuple(runtime, length);
  271. IRubyObject addressArray = addrFor(context, tuple.sender, false);
  272. return runtime.newArray(tuple.result, addressArray);
  273. } catch (UnknownHostException e) {
  274. throw SocketUtils.sockerr(runtime, "recvfrom: name or service not known");
  275. } catch (PortUnreachableException e) {
  276. throw runtime.newErrnoECONNREFUSEDError();
  277. } catch (IOException e) {
  278. throw SocketUtils.sockerr(runtime, "recvfrom: name or service not known");
  279. }
  280. }
  281. /**
  282. * Overrides IPSocket#recvfrom
  283. */
  284. @Override
  285. public IRubyObject recvfrom(ThreadContext context, IRubyObject _length, IRubyObject _flags) {
  286. // TODO: handle flags
  287. return recvfrom(context, _length);
  288. }
  289. /**
  290. * Overrides BasicSocket#recv
  291. */
  292. @Override
  293. public IRubyObject recv(ThreadContext context, IRubyObject _length) {
  294. Ruby runtime = context.runtime;
  295. try {
  296. return doReceive(runtime, RubyNumeric.fix2int(_length));
  297. } catch (IOException e) {
  298. throw SocketUtils.sockerr(runtime, "recv: name or service not known");
  299. }
  300. }
  301. /**
  302. * Overrides BasicSocket#recv
  303. */
  304. @Override
  305. public IRubyObject recv(ThreadContext context, IRubyObject _length, IRubyObject _flags) {
  306. // TODO: implement flags
  307. return recv(context, _length);
  308. }
  309. private ReceiveTuple doReceiveTuple(Ruby runtime, int length) throws IOException {
  310. ReceiveTuple tuple = new ReceiveTuple();
  311. if (this.multicastStateManager == null) {
  312. doReceive(runtime, length, tuple);
  313. } else {
  314. doReceiveMulticast(runtime, length, tuple);
  315. }
  316. return tuple;
  317. }
  318. private ReceiveTuple doReceiveNonblockTuple(Ruby runtime, int length) throws IOException {
  319. DatagramChannel channel = (DatagramChannel)getChannel();
  320. synchronized (channel.blockingLock()) {
  321. boolean oldBlocking = channel.isBlocking();
  322. channel.configureBlocking(false);
  323. try {
  324. return doReceiveTuple(runtime, length);
  325. } finally {
  326. channel.configureBlocking(oldBlocking);
  327. }
  328. }
  329. }
  330. private static class ReceiveTuple {
  331. ReceiveTuple() {}
  332. ReceiveTuple(RubyString result, InetSocketAddress sender) {
  333. this.result = result;
  334. this.sender = sender;
  335. }
  336. RubyString result;
  337. InetSocketAddress sender;
  338. }
  339. private IRubyObject doReceive(Ruby runtime, int length) throws IOException {
  340. return doReceive(runtime, length, null);
  341. }
  342. private IRubyObject doReceive(Ruby runtime, int length, ReceiveTuple tuple) throws IOException {
  343. DatagramChannel channel = (DatagramChannel)getChannel();
  344. ByteBuffer buf = ByteBuffer.allocate(length);
  345. InetSocketAddress sender = (InetSocketAddress)channel.receive(buf);
  346. if (sender == null) {
  347. // noblocking receive
  348. if (runtime.is1_9()) {
  349. throw runtime.newErrnoEAGAINReadableError("recvfrom(2) would block");
  350. } else {
  351. throw runtime.newErrnoEAGAINError("recvfrom(2) would block");
  352. }
  353. }
  354. // see JRUBY-4678
  355. if (sender == null) {
  356. throw runtime.newErrnoECONNRESETError();
  357. }
  358. RubyString result = runtime.newString(new ByteList(buf.array(), 0, buf.position()));
  359. if (tuple != null) {
  360. tuple.result = result;
  361. tuple.sender = sender;
  362. }
  363. return result;
  364. }
  365. private IRubyObject doReceiveMulticast(Ruby runtime, int length, ReceiveTuple tuple) throws IOException {
  366. byte[] buf2 = new byte[length];
  367. DatagramPacket recv = new DatagramPacket(buf2, buf2.length);
  368. MulticastSocket ms = this.multicastStateManager.getMulticastSocket();
  369. try {
  370. ms.receive(recv);
  371. } catch (IllegalBlockingModeException ibme) {
  372. // MulticastSocket does not support nonblocking
  373. // TODO: Use Java 7 NIO.2 DatagramChannel to do multicast
  374. if (runtime.is1_9()) {
  375. throw runtime.newErrnoEAGAINReadableError("multicast UDP does not support nonblocking");
  376. } else {
  377. throw runtime.newErrnoEAGAINError("multicast UDP does not support nonblocking");
  378. }
  379. }
  380. InetSocketAddress sender = (InetSocketAddress) recv.getSocketAddress();
  381. // see JRUBY-4678
  382. if (sender == null) {
  383. throw runtime.newErrnoECONNRESETError();
  384. }
  385. RubyString result = runtime.newString(new ByteList(recv.getData(), 0, recv.getLength()));
  386. if (tuple != null) {
  387. tuple.result = result;
  388. tuple.sender = sender;
  389. }
  390. return result;
  391. }
  392. @Deprecated
  393. public IRubyObject bind(IRubyObject host, IRubyObject port) {
  394. return bind(getRuntime().getCurrentContext(), host, port);
  395. }
  396. @Deprecated
  397. public IRubyObject connect(IRubyObject host, IRubyObject port) {
  398. return connect(getRuntime().getCurrentContext(), host, port);
  399. }
  400. @Deprecated
  401. public IRubyObject recvfrom(IRubyObject[] args) {
  402. return recvfrom(getRuntime().getCurrentContext(), args);
  403. }
  404. @Deprecated
  405. public IRubyObject send(IRubyObject[] args) {
  406. return send(getRuntime().getCurrentContext(), args);
  407. }
  408. @Deprecated
  409. public static IRubyObject open(IRubyObject recv, IRubyObject[] args, Block block) {
  410. return open(recv.getRuntime().getCurrentContext(), recv, args, block);
  411. }
  412. }// RubyUDPSocket