Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 862ee1c7f0ca
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: bbceb691e009
Choose a head ref
  • 8 commits
  • 9 files changed
  • 2 contributors

Commits on Sep 8, 2016

  1. Copy the full SHA
    b25773f View commit details
  2. Fix passing socket option in a form of SO_OPTION

    For example:
    Socket::Option.new(:UNSPEC, :SOCKET, :SO_LINGER, [0, 0].pack('i*')
    Fixes #3438
    etehtsea committed Sep 8, 2016
    Copy the full SHA
    a87d873 View commit details
  3. More robust conversion and value-checking.

    Covers address family, protocol family, socket type, and protocol.
    headius authored and etehtsea committed Sep 8, 2016
    Copy the full SHA
    250f44a View commit details
  4. Fix passing socket level in a form of SOL_LEVEL

    For example:
    Socket::Option.int(:AF_UNSPEC, :SOL_SOCKET, :LINGER, 1)
    Fixes #3438
    etehtsea committed Sep 8, 2016
    Copy the full SHA
    8fac9c6 View commit details
  5. More robust conversion and value-checking

    Also cover socket level and socket option
    etehtsea committed Sep 8, 2016
    Copy the full SHA
    abb1a5f View commit details
  6. Copy the full SHA
    40f6b41 View commit details
  7. Copy the full SHA
    1902354 View commit details

Commits on Sep 12, 2016

  1. Merge pull request #4119 from etehtsea/getset-option-linger

    Full Socket::Option support
    headius authored Sep 12, 2016
    Copy the full SHA
    bbceb69 View commit details
108 changes: 83 additions & 25 deletions core/src/main/java/org/jruby/ext/socket/Option.java
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
import org.jruby.util.Sprintf;
import org.jruby.util.TypeConverter;

import java.nio.ByteBuffer;
import java.text.NumberFormat;
@@ -42,19 +43,17 @@ public Option(Ruby runtime, RubyClass klass) {
super(runtime, klass);
}

public Option(Ruby runtime, ProtocolFamily family, SocketLevel level, SocketOption option, int data) {
public Option(Ruby runtime, ProtocolFamily family, SocketLevel level, SocketOption option, ByteList data) {
this(runtime, (RubyClass)runtime.getClassFromPath("Socket::Option"), family, level, option, data);
}

public Option(Ruby runtime, RubyClass klass, ProtocolFamily family, SocketLevel level, SocketOption option, int data) {
public Option(Ruby runtime, RubyClass klass, ProtocolFamily family, SocketLevel level, SocketOption option, ByteList data) {
super(runtime, klass);

this.family = family;
this.level = level;
this.option = option;
this.intData = data;
ByteList result = new ByteList(4);
this.data = Pack.packInt_i(result, data);
this.data = data;
}

@JRubyMethod(required = 4, visibility = Visibility.PRIVATE)
@@ -63,7 +62,7 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
level = SocketUtils.levelFromArg(args[1]);
option = SocketUtils.optionFromArg(args[2]);
data = args[3].convertToString().getByteList();
intData = Pack.unpackInt_i(ByteBuffer.wrap(data.bytes()));

return this;
}

@@ -95,7 +94,7 @@ public IRubyObject inspect(ThreadContext context) {

buf
.append(metaClass.getRealClass().getName())
.append(' ')
.append(": ")
.append(noPrefix(family));

if (level == SocketLevel.SOL_SOCKET) {
@@ -146,65 +145,117 @@ private String optionValue() {
case SO_DONTROUTE:
case SO_RCVLOWAT:
case SO_SNDLOWAT:
return String.valueOf(intData);
return String.valueOf(unpackInt(data));

case SO_LINGER:
return intData == -1 ? "off" :
intData == 0 ? "on" :
"on(" + intData + ")";
int[] linger = Option.unpackLinger(data);

return ((linger[0] == 0) ? "off " : "on ") + linger[1] + "sec";

case SO_RCVTIMEO:
case SO_SNDTIMEO:
return Sprintf.getNumberFormat(Locale.getDefault()).format(intData / 1000.0);
return Sprintf.getNumberFormat(Locale.getDefault()).format(unpackInt(data) / 1000.0);

case SO_ERROR:
return Errno.valueOf(intData).description();
return Errno.valueOf(unpackInt(data)).description();

case SO_TYPE:
return Sock.valueOf(intData).description();
return Sock.valueOf(unpackInt(data)).description();
}

return "";
}

public static ByteList packInt(int i) {
ByteList result = new ByteList(4);
Pack.packInt_i(result, i);
return result;
}

public static ByteList packLinger(int vonoff, int vsecs) {
ByteList result = new ByteList(8);
Pack.packInt_i(result, vonoff);
Pack.packInt_i(result, vsecs);
return result;
}

public static int unpackInt(ByteList data) {
return Pack.unpackInt_i(ByteBuffer.wrap(data.unsafeBytes(), data.begin(), data.realSize()));
}

public static int[] unpackLinger(ByteList data) {
ByteList result = new ByteList(8);
ByteBuffer buf = ByteBuffer.wrap(data.unsafeBytes(), data.begin(), data.realSize());
int vonoff = Pack.unpackInt_i(buf);
int vsecs = Pack.unpackInt_i(buf);
return new int[] {vonoff, vsecs};
}

@JRubyMethod(name = "int", required = 4, meta = true)
public static IRubyObject rb_int(ThreadContext context, IRubyObject self, IRubyObject[] args) {
ProtocolFamily family = SocketUtils.protocolFamilyFromArg(args[0]);
SocketLevel level = SocketUtils.levelFromArg(args[1]);
SocketOption option = SocketUtils.optionFromArg(args[2]);
int intData = RubyNumeric.fix2int(args[3]);
ByteList data = packInt(RubyNumeric.fix2int(args[3]));

return new Option(context.getRuntime(), family, level, option, intData);
return new Option(context.getRuntime(), family, level, option, data);
}

@JRubyMethod(name = "int")
public IRubyObject asInt(ThreadContext context) {
return context.getRuntime().newFixnum((int) intData);
final Ruby runtime = context.getRuntime();

validateDataSize(runtime, data, 4);

return runtime.newFixnum(unpackInt(data));
}

@JRubyMethod(required = 4, meta = true)
public static IRubyObject bool(ThreadContext context, IRubyObject self, IRubyObject[] args) {
ProtocolFamily family = SocketUtils.protocolFamilyFromArg(args[0]);
SocketLevel level = SocketUtils.levelFromArg(args[1]);
SocketOption option = SocketUtils.optionFromArg(args[2]);
int intData = args[3].isTrue() ? 1 : 0;
ByteList data = packInt(args[3].isTrue() ? 1 : 0);

return new Option(context.getRuntime(), family, level, option, intData);
return new Option(context.getRuntime(), family, level, option, data);
}

@JRubyMethod
public IRubyObject bool(ThreadContext context) {
return context.getRuntime().newBoolean(intData != 0);
final Ruby runtime = context.getRuntime();

validateDataSize(runtime, data, 4);

return runtime.newBoolean(unpackInt(data) != 0);
}

@JRubyMethod(meta = true)
public IRubyObject linger(ThreadContext context, IRubyObject self) {
return context.nil;
}
public static IRubyObject linger(ThreadContext context, IRubyObject self, IRubyObject vonoff, IRubyObject vsecs) {
ProtocolFamily family = ProtocolFamily.PF_UNSPEC;
SocketLevel level = SocketLevel.SOL_SOCKET;
SocketOption option = SocketOption.SO_LINGER;
int coercedVonoff;

if (!TypeConverter.checkIntegerType(context, vonoff).isNil()) {
coercedVonoff = vonoff.convertToInteger().getIntValue();
} else {
coercedVonoff = vonoff.isTrue() ? 1 : 0;
}

ByteList data = packLinger(coercedVonoff, vsecs.convertToInteger().getIntValue());

return new Option(context.getRuntime(), family, level, option, data);
}

@JRubyMethod
public IRubyObject linger(ThreadContext context) {
return context.nil;
final Ruby runtime = context.runtime;

validateDataSize(runtime, data, 8);

int[] linger = Option.unpackLinger(data);

return runtime.newArray(runtime.newBoolean(linger[0] != 0), runtime.newFixnum(linger[1]));
}

@JRubyMethod
@@ -217,9 +268,16 @@ public IRubyObject to_s(ThreadContext context) {
return RubyString.newString(context.runtime, data);
}

private static void validateDataSize(Ruby runtime, ByteList data, int size) {
int realSize = data.realSize();

if (realSize != size) {
throw runtime.newTypeError("size differ. expected as sizeof(int)=" + size + " but " + realSize);
}
}

private ProtocolFamily family;
private SocketLevel level;
private SocketOption option;
private ByteList data;
private long intData;
}
38 changes: 28 additions & 10 deletions core/src/main/java/org/jruby/ext/socket/RubyBasicSocket.java
Original file line number Diff line number Diff line change
@@ -266,8 +266,21 @@ public IRubyObject getsockopt(ThreadContext context, IRubyObject _level, IRubyOb
}

int value = SocketType.forChannel(channel).getSocketOption(channel, opt);
ByteList packedValue;

return new Option(runtime, ProtocolFamily.PF_INET, level, opt, value);
if (opt == SocketOption.SO_LINGER) {
if (value == -1) {
// Hardcode to 0, because Java Socket API drops actual value
// if lingering is disabled
packedValue = Option.packLinger(0, 0);
} else {
packedValue = Option.packLinger(1, value);
}
} else {
packedValue = Option.packInt(value);
}

return new Option(runtime, ProtocolFamily.PF_INET, level, opt, packedValue);

default:
throw runtime.newErrnoENOPROTOOPTError();
@@ -278,6 +291,16 @@ public IRubyObject getsockopt(ThreadContext context, IRubyObject _level, IRubyOb
}
}

@JRubyMethod
public IRubyObject setsockopt(ThreadContext context, IRubyObject option) {
if (option instanceof Option) {
Option rsockopt = (Option) option;
return setsockopt(context, rsockopt.level(context), rsockopt.optname(context), rsockopt.data(context));
} else {
throw context.runtime.newArgumentError(option.toString() + " is not a Socket::Option");
}
}

@JRubyMethod
public IRubyObject setsockopt(ThreadContext context, IRubyObject _level, IRubyObject _opt, IRubyObject val) {
Ruby runtime = context.runtime;
@@ -297,17 +320,12 @@ public IRubyObject setsockopt(ThreadContext context, IRubyObject _level, IRubyOb
case SOL_UDP:

if (opt == SocketOption.SO_LINGER) {
if(val instanceof RubyBoolean && !val.isTrue()) {
socketType.setSoLinger(channel, false, 0);
if (val instanceof RubyString) {
int[] linger = Option.unpackLinger(val.convertToString().getByteList());
socketType.setSoLinger(channel, linger[0] != 0, linger[1]);
} else {
int num = asNumber(val);
if(num == -1) {
socketType.setSoLinger(channel, false, 0);
} else {
socketType.setSoLinger(channel, true, num);
}
throw runtime.newErrnoEINVALError("setsockopt(2)");
}

} else {
socketType.setSocketOption(channel, opt, asNumber(val));
}
13 changes: 6 additions & 7 deletions core/src/main/java/org/jruby/ext/socket/SocketType.java
Original file line number Diff line number Diff line change
@@ -177,7 +177,7 @@ public SocketAddress getLocalSocketAddress(Channel channel) {
private DatagramSocket toSocket(Channel channel) {
return ((DatagramChannel)channel).socket();
}

public int getSoTimeout(Channel channel) throws IOException {
return toSocket(channel).getSoTimeout();
}
@@ -241,14 +241,14 @@ public void shutdownOutput(Channel channel) throws IOException {
},

UNKNOWN(Sock.SOCK_STREAM);

public static SocketType forChannel(Channel channel) {
if (channel instanceof SocketChannel) {
return SOCKET;

} else if (channel instanceof ServerSocketChannel) {
return SERVER;

} else if (channel instanceof DatagramChannel) {
return DATAGRAM;

@@ -301,7 +301,7 @@ public SocketAddress getLocalSocketAddress(Channel channel) {
public Sock getSocketType() {
return sock;
}

public int getSocketOption(Channel channel, SocketOption option) throws IOException {
switch (option) {

@@ -312,8 +312,7 @@ public int getSocketOption(Channel channel, SocketOption option) throws IOExcept
return getKeepAlive(channel) ? 1 : 0;

case SO_LINGER: {
int linger = getSoLinger(channel);
return linger < 0 ? 0 : linger;
return getSoLinger(channel);
}

case SO_OOBINLINE:
Loading