package org.jruby.ext.socket;
import jnr.unixsocket.UnixServerSocketChannel;
import jnr.unixsocket.UnixSocketAddress;
import jnr.unixsocket.UnixSocketChannel;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
@JRubyClass(name="UNIXServer", parent="UNIXSocket")
public class RubyUNIXServer extends RubyUNIXSocket {
private static ObjectAllocator UNIXSERVER_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyUNIXServer(runtime, klass);
}
};
static void createUNIXServer(Ruby runtime) {
RubyClass rb_cUNIXServer = runtime.defineClass("UNIXServer", runtime.getClass("UNIXSocket"), UNIXSERVER_ALLOCATOR);
runtime.getObject().setConstant("UNIXserver", rb_cUNIXServer);
rb_cUNIXServer.defineAnnotatedMethods(RubyUNIXServer.class);
}
public RubyUNIXServer(Ruby runtime, RubyClass type) {
super(runtime, type);
}
@JRubyMethod(visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject path) {
init_unixsock(context.runtime, path, true);
return this;
}
@JRubyMethod
public IRubyObject accept(ThreadContext context) {
Ruby runtime = context.runtime;
try {
while (true) {
boolean ready = context.getThread().select(this, SelectionKey.OP_ACCEPT);
if (!ready) {
context.pollThreadEvents();
} else {
UnixSocketChannel socketChannel = asUnixServer().accept();
RubyUNIXSocket sock = (RubyUNIXSocket)(Helpers.invoke(context, runtime.getClass("UNIXSocket"), "allocate"));
sock.init_sock(context.runtime, socketChannel, "");
return sock;
}
}
} catch (IOException ioe) {
throw context.runtime.newIOErrorFromException(ioe);
}
}
@JRubyMethod
public IRubyObject accept_nonblock(ThreadContext context) {
return accept_nonblock(context, context.runtime, true);
}
@JRubyMethod
public IRubyObject accept_nonblock(ThreadContext context, IRubyObject opts) {
return accept_nonblock(context, context.runtime, extractExceptionArg(context, opts));
}
public IRubyObject accept_nonblock(ThreadContext context, Ruby runtime, boolean ex) {
SelectableChannel selectable = (SelectableChannel)getChannel();
synchronized (selectable.blockingLock()) {
boolean oldBlocking = selectable.isBlocking();
try {
selectable.configureBlocking(false);
try {
UnixSocketChannel socketChannel = ((UnixServerSocketChannel) selectable).accept();
if (socketChannel == null) {
if (!ex) return runtime.newSymbol("wait_readable");
throw runtime.newErrnoEAGAINReadableError("accept(2) would block");
}
RubyUNIXSocket sock = (RubyUNIXSocket)(Helpers.invoke(context, runtime.getClass("UNIXSocket"), "allocate"));
sock.init_sock(context.runtime, socketChannel, "");
return sock;
} finally {
selectable.configureBlocking(oldBlocking);
}
} catch (IOException ioe) {
if (ioe.getMessage().equals("accept failed: Resource temporarily unavailable")) {
if (!ex) return runtime.newSymbol("wait_readable");
throw runtime.newErrnoEAGAINReadableError("accept");
}
throw context.runtime.newIOErrorFromException(ioe);
}
}
}
@JRubyMethod
public IRubyObject listen(ThreadContext context, IRubyObject log) {
return context.runtime.newFixnum(0);
}
@JRubyMethod
public IRubyObject sysaccept(ThreadContext context) {
RubyUNIXSocket socket = (RubyUNIXSocket) accept(context);
return context.runtime.newFixnum(((UnixSocketChannel) socket.getChannel()).getFD());
}
@JRubyMethod
public IRubyObject path(ThreadContext context) {
return context.runtime.newString(openFile.getPath());
}
@JRubyMethod
public IRubyObject addr(ThreadContext context) {
Ruby runtime = context.runtime;
return runtime.newArray(
runtime.newString("AF_UNIX"),
runtime.newString(openFile.getPath()));
}
@JRubyMethod
public IRubyObject peeraddr(ThreadContext context) {
throw context.runtime.newErrnoENOTCONNError();
}
@Override
protected UnixSocketAddress getUnixSocketAddress() {
SocketAddress socketAddress = ((UnixServerSocketChannel)getChannel()).getLocalSocketAddress();
if (socketAddress instanceof UnixSocketAddress) return (UnixSocketAddress) socketAddress;
return null;
}
@Override
protected UnixSocketAddress getUnixRemoteSocket() {
SocketAddress socketAddress = ((UnixServerSocketChannel)getChannel()).getLocalSocketAddress();
if (socketAddress instanceof UnixSocketAddress) return (UnixSocketAddress) socketAddress;
return null;
}
private UnixServerSocketChannel asUnixServer() {
return (UnixServerSocketChannel)getChannel();
}
}