Skip to content

Commit

Permalink
initial support for OpenSSL::SSL::Session (id, time, timeout work, …
Browse files Browse the repository at this point in the history
…no to_der/to_pem)

closes #26
  • Loading branch information
kares committed May 21, 2015
1 parent e9eeba8 commit 955f15a
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 18 deletions.
7 changes: 1 addition & 6 deletions lib/jopenssl18/openssl/ssl-internal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,7 @@ def post_connection_check(hostname)
end
return true
end

def session
SSL::Session.new(self)
rescue SSL::Session::SessionError
nil
end

end

class SSLServer
Expand Down
5 changes: 0 additions & 5 deletions lib/jopenssl19/openssl/ssl-internal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,6 @@ def post_connection_check(hostname)
return true
end

def session
SSL::Session.new(self)
rescue SSL::Session::SessionError
nil
end
end

class SSLServer
Expand Down
5 changes: 0 additions & 5 deletions lib/jopenssl21/openssl/ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,6 @@ def post_connection_check(hostname)
return true
end

def session
SSL::Session.new(self)
rescue SSL::Session::SessionError
nil
end
end

##
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/jruby/ext/openssl/SSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public static void createSSL(final Ruby runtime, final RubyModule OpenSSL) {

SSLContext.createSSLContext(runtime, SSL);
SSLSocket.createSSLSocket(runtime, SSL);
SSLSession.createSession(runtime, SSL);
}

public static RaiseException newSSLError(Ruby runtime, Exception exception) {
Expand Down
153 changes: 153 additions & 0 deletions src/main/java/org/jruby/ext/openssl/SSLSession.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* The MIT License
*
* Copyright 2015 Karol Bucek.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jruby.ext.openssl;

import java.util.Arrays;
import javax.net.ssl.SSLSessionContext;

import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.RubyTime;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;

import static org.jruby.ext.openssl.OpenSSL._OpenSSLError;
import static org.jruby.ext.openssl.OpenSSL.warn;
import static org.jruby.ext.openssl.SSL._SSL;

/**
* OpenSSL::SSL::Session
*
* @author kares
*/
public class SSLSession extends RubyObject {

private static final ObjectAllocator SESSION_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new SSLSession(runtime, klass);
}
};

public static void createSession(final Ruby runtime, final RubyModule SSL) { // OpenSSL::SSL
RubyClass Session = SSL.defineClassUnder("Session", runtime.getObject(), SESSION_ALLOCATOR);
Session.defineAnnotatedMethods(SSLSession.class);
// OpenSSL::SSL::Session::SessionError
RubyClass OpenSSLError = _OpenSSLError(runtime);
Session.defineClassUnder("SessionError", OpenSSLError, OpenSSLError.getAllocator());
}

private javax.net.ssl.SSLSession sslSession;

SSLSession(Ruby runtime, RubyClass metaClass) {
super(runtime, metaClass);
}

SSLSession(Ruby runtime) {
this(runtime, (RubyClass) _SSL(runtime).getConstantAt("Session"));
}

@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public IRubyObject initialize(final ThreadContext context, final IRubyObject arg) {
final Ruby runtime = context.runtime;

if ( arg instanceof SSLSocket ) {
return initializeImpl(context, (SSLSocket) arg);
}

throw runtime.newNotImplementedError("Session#initialize with " + arg.getMetaClass().getName());
}

SSLSession initializeImpl(final ThreadContext context, final SSLSocket socket) {
sslSession = socket.getSession();
return this;
}

final javax.net.ssl.SSLSession sslSession() {
return sslSession;
}

@JRubyMethod(name = "==")
public IRubyObject op_eqq(final ThreadContext context, final IRubyObject other) {
if ( other instanceof SSLSession ) {
final SSLSession that = (SSLSession) other;
if ( this.sslSession.getProtocol().equals( that.sslSession.getProtocol() ) ) {
if ( Arrays.equals( this.sslSession.getId(), that.sslSession.getId() ) ) {
return context.runtime.getTrue();
}
}
}
return context.runtime.getFalse();
}

@JRubyMethod(name = "id")
public RubyString id(final ThreadContext context) {
final byte[] id = sslSession().getId();
return context.runtime.newString( new ByteList(id) );
}

@JRubyMethod(name = "id=")
public IRubyObject set_id(final ThreadContext context, IRubyObject id) {
warn(context, "WARNING: Session#id= is not supported (read-only)");
return context.nil;
}

@JRubyMethod(name = "time")
public RubyTime time(final ThreadContext context) {
final long time = sslSession().getCreationTime();
return RubyTime.newTime(context.runtime, time);
}

@JRubyMethod(name = "time=")
public IRubyObject set_time(final ThreadContext context, IRubyObject time) {
warn(context, "WARNING: Session#time= is not supported (read-only)");
return context.nil;
}

@JRubyMethod(name = "timeout")
public IRubyObject timeout(final ThreadContext context) {
final SSLSessionContext sessionContext = sslSession().getSessionContext();
if ( sessionContext == null ) return context.nil;
return context.runtime.newFixnum(sessionContext.getSessionTimeout());
}

@JRubyMethod(name = "timeout=")
public IRubyObject set_timeout(final ThreadContext context, IRubyObject timeout) {
final SSLSessionContext sessionContext = sslSession().getSessionContext();
if ( sessionContext == null ) {
warn(context, "WARNING: can not set Session#timeout=("+ timeout +") no session context");
return context.nil;
}
final long t = timeout.convertToInteger().getLongValue();
sessionContext.setSessionTimeout((int) t); // in seconds as well
return timeout;
}

}
18 changes: 16 additions & 2 deletions src/main/java/org/jruby/ext/openssl/SSLSocket.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;

import org.jruby.Ruby;
import org.jruby.RubyArray;
Expand Down Expand Up @@ -178,7 +177,7 @@ private SSLEngine ossl_ssl_setup(final ThreadContext context)
final int peerPort = socket.getPort();
engine = sslContext.createSSLEngine(peerHost, peerPort);

final SSLSession session = engine.getSession();
final javax.net.ssl.SSLSession session = engine.getSession();
peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
netData = ByteBuffer.allocate(session.getPacketBufferSize());
Expand Down Expand Up @@ -880,6 +879,21 @@ public IRubyObject session_reused_p() {
return getRuntime().getNil(); // throw new UnsupportedOperationException();
}

javax.net.ssl.SSLSession getSession() {
return engine == null ? null : engine.getSession();
}

private transient SSLSession session;

@JRubyMethod(name = "session")
public IRubyObject session(final ThreadContext context) {
if ( getSession() == null ) return context.nil;
if ( session == null ) {
return session = new SSLSession(context.runtime).initializeImpl(context, this);
}
return session;
}

@JRubyMethod(name = "session=")
public IRubyObject set_session(IRubyObject session) {
warn(getRuntime().getCurrentContext(), "WARNING: SSLSocket#session= is not supported");
Expand Down
37 changes: 37 additions & 0 deletions src/test/ruby/ssl/test_session.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# coding: US-ASCII
require File.expand_path('test_helper', File.dirname(__FILE__))

class TestSSLSession < TestCase
include SSLTestHelper

def test_session
start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
sock = TCPSocket.new("127.0.0.1", port)
ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.sync_close = true
ssl.connect

assert ssl.session.is_a?(OpenSSL::SSL::Session)
assert ssl.session.equal? session = ssl.session

assert session.id.is_a?(String)
assert_equal 32, session.id.length
assert session.time.is_a?(Time)

assert session.timeout >= 0

session.timeout = 5
assert_equal 5, session.timeout

assert session == OpenSSL::SSL::Session.new(ssl)

ssl.close
end
end

def test_exposes_session_error
OpenSSL::SSL::Session::SessionError
end

end

0 comments on commit 955f15a

Please sign in to comment.