mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Merge branch 'minissl' into just-dash-w
Conflicts: ext/puma_http11/puma_http11.c lib/puma/cli.rb lib/puma/server.rb
This commit is contained in:
commit
faf7f9916c
11 changed files with 639 additions and 18 deletions
6
Rakefile
6
Rakefile
|
@ -60,6 +60,8 @@ file 'ext/puma_http11/org/jruby/puma/Http11Parser.java' => ['ext/puma_http11/htt
|
|||
end
|
||||
task :ragel => ['ext/puma_http11/org/jruby/puma/Http11Parser.java']
|
||||
|
||||
if !IS_JRUBY
|
||||
|
||||
# compile extensions using rake-compiler
|
||||
# C (MRI, Rubinius)
|
||||
Rake::ExtensionTask.new("puma_http11", HOE.spec) do |ext|
|
||||
|
@ -77,11 +79,15 @@ Rake::ExtensionTask.new("puma_http11", HOE.spec) do |ext|
|
|||
CLEAN.include "lib/puma/puma_http11.rb"
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
# Java (JRuby)
|
||||
Rake::JavaExtensionTask.new("puma_http11", HOE.spec) do |ext|
|
||||
ext.lib_dir = "lib/puma"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# the following is a fat-binary stub that will be used when
|
||||
# require 'puma/puma_http11' and will use either 1.8 or 1.9 version depending
|
||||
# on RUBY_VERSION
|
||||
|
|
BIN
examples/puma/keystore.jks
Normal file
BIN
examples/puma/keystore.jks
Normal file
Binary file not shown.
|
@ -6,10 +6,12 @@ import org.jruby.Ruby;
|
|||
import org.jruby.runtime.load.BasicLibraryService;
|
||||
|
||||
import org.jruby.puma.Http11;
|
||||
import org.jruby.puma.MiniSSL;
|
||||
|
||||
public class PumaHttp11Service implements BasicLibraryService {
|
||||
public boolean basicLoad(final Ruby runtime) throws IOException {
|
||||
Http11.createHttp11(runtime);
|
||||
MiniSSL.createMiniSSL(runtime);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,7 @@ require 'mkmf'
|
|||
|
||||
dir_config("puma_http11")
|
||||
|
||||
$defs.push "-Wno-deprecated-declarations"
|
||||
$libs += " -lssl -lcrypto "
|
||||
|
||||
create_makefile("puma/puma_http11")
|
||||
|
|
189
ext/puma_http11/mini_ssl.c
Normal file
189
ext/puma_http11/mini_ssl.c
Normal file
|
@ -0,0 +1,189 @@
|
|||
#include <ruby.h>
|
||||
#include <rubyio.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
typedef struct {
|
||||
BIO* read;
|
||||
BIO* write;
|
||||
SSL* ssl;
|
||||
SSL_CTX* ctx;
|
||||
} ms_conn;
|
||||
|
||||
void engine_free(ms_conn* conn) {
|
||||
BIO_free(conn->read);
|
||||
BIO_free(conn->write);
|
||||
|
||||
free(conn);
|
||||
}
|
||||
|
||||
ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
|
||||
ms_conn* conn;
|
||||
|
||||
*obj = Data_Make_Struct(klass, ms_conn, 0, engine_free, conn);
|
||||
|
||||
conn->read = BIO_new(BIO_s_mem());
|
||||
BIO_set_nbio(conn->read, 1);
|
||||
|
||||
conn->write = BIO_new(BIO_s_mem());
|
||||
BIO_set_nbio(conn->write, 1);
|
||||
|
||||
conn->ssl = 0;
|
||||
conn->ctx = 0;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
VALUE engine_init_server(VALUE self, VALUE key, VALUE cert) {
|
||||
VALUE obj;
|
||||
ms_conn* conn = engine_alloc(self, &obj);
|
||||
|
||||
StringValue(key);
|
||||
StringValue(cert);
|
||||
|
||||
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
conn->ctx = ctx;
|
||||
|
||||
SSL_CTX_use_certificate_file(ctx, RSTRING_PTR(cert), SSL_FILETYPE_PEM);
|
||||
SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
|
||||
/* SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); */
|
||||
|
||||
SSL* ssl = SSL_new(ctx);
|
||||
conn->ssl = ssl;
|
||||
|
||||
/* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
|
||||
|
||||
SSL_set_bio(ssl, conn->read, conn->write);
|
||||
|
||||
SSL_set_accept_state(ssl);
|
||||
return obj;
|
||||
}
|
||||
|
||||
VALUE engine_init_client(VALUE klass) {
|
||||
VALUE obj;
|
||||
ms_conn* conn = engine_alloc(klass, &obj);
|
||||
|
||||
conn->ctx = SSL_CTX_new(DTLSv1_method());
|
||||
conn->ssl = SSL_new(conn->ctx);
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
|
||||
|
||||
SSL_set_bio(conn->ssl, conn->read, conn->write);
|
||||
|
||||
SSL_set_connect_state(conn->ssl);
|
||||
return obj;
|
||||
}
|
||||
|
||||
VALUE engine_inject(VALUE self, VALUE str) {
|
||||
ms_conn* conn;
|
||||
long used;
|
||||
|
||||
Data_Get_Struct(self, ms_conn, conn);
|
||||
|
||||
StringValue(str);
|
||||
|
||||
used = BIO_write(conn->read, RSTRING_PTR(str), RSTRING_LEN(str));
|
||||
|
||||
if(used == 0 || used == -1) {
|
||||
return Qfalse;
|
||||
}
|
||||
|
||||
return INT2FIX(used);
|
||||
}
|
||||
|
||||
static VALUE eError;
|
||||
|
||||
void raise_error(SSL* ssl, int result) {
|
||||
int error = SSL_get_error(ssl, result);
|
||||
char* msg = ERR_error_string(error, NULL);
|
||||
|
||||
ERR_clear_error();
|
||||
rb_raise(eError, "OpenSSL error: %s - %d", msg, error);
|
||||
}
|
||||
|
||||
VALUE engine_read(VALUE self) {
|
||||
ms_conn* conn;
|
||||
char buf[512];
|
||||
int bytes, n;
|
||||
|
||||
Data_Get_Struct(self, ms_conn, conn);
|
||||
|
||||
bytes = SSL_read(conn->ssl, (void*)buf, sizeof(buf));
|
||||
|
||||
if(bytes > 0) {
|
||||
return rb_str_new(buf, bytes);
|
||||
}
|
||||
|
||||
if(SSL_want_read(conn->ssl)) return Qnil;
|
||||
|
||||
if(SSL_get_error(conn->ssl, bytes) == SSL_ERROR_ZERO_RETURN) {
|
||||
rb_eof_error();
|
||||
}
|
||||
|
||||
raise_error(conn->ssl, bytes);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE engine_write(VALUE self, VALUE str) {
|
||||
ms_conn* conn;
|
||||
char buf[512];
|
||||
int bytes;
|
||||
|
||||
Data_Get_Struct(self, ms_conn, conn);
|
||||
|
||||
StringValue(str);
|
||||
|
||||
bytes = SSL_write(conn->ssl, (void*)RSTRING_PTR(str), RSTRING_LEN(str));
|
||||
if(bytes > 0) {
|
||||
return INT2FIX(bytes);
|
||||
}
|
||||
|
||||
if(SSL_want_write(conn->ssl)) return Qnil;
|
||||
|
||||
raise_error(conn->ssl, bytes);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE engine_extract(VALUE self) {
|
||||
ms_conn* conn;
|
||||
int bytes;
|
||||
size_t pending;
|
||||
char buf[512];
|
||||
|
||||
Data_Get_Struct(self, ms_conn, conn);
|
||||
|
||||
pending = BIO_pending(conn->write);
|
||||
if(pending > 0) {
|
||||
bytes = BIO_read(conn->write, buf, sizeof(buf));
|
||||
if(bytes > 0) {
|
||||
return rb_str_new(buf, bytes);
|
||||
} else if(!BIO_should_retry(conn->write)) {
|
||||
raise_error(conn->ssl, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
void Init_mini_ssl(VALUE puma) {
|
||||
SSL_library_init();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
SSL_load_error_strings();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
VALUE mod = rb_define_module_under(puma, "MiniSSL");
|
||||
VALUE eng = rb_define_class_under(mod, "Engine", rb_cObject);
|
||||
|
||||
eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
|
||||
|
||||
rb_define_singleton_method(eng, "server", engine_init_server, 2);
|
||||
rb_define_singleton_method(eng, "client", engine_init_client, 0);
|
||||
|
||||
rb_define_method(eng, "inject", engine_inject, 1);
|
||||
rb_define_method(eng, "read", engine_read, 0);
|
||||
|
||||
rb_define_method(eng, "write", engine_write, 1);
|
||||
rb_define_method(eng, "extract", engine_extract, 0);
|
||||
}
|
289
ext/puma_http11/org/jruby/puma/MiniSSL.java
Normal file
289
ext/puma_http11/org/jruby/puma/MiniSSL.java
Normal file
|
@ -0,0 +1,289 @@
|
|||
package org.jruby.puma;
|
||||
|
||||
import org.jruby.Ruby;
|
||||
import org.jruby.RubyClass;
|
||||
import org.jruby.RubyHash;
|
||||
import org.jruby.RubyModule;
|
||||
import org.jruby.RubyNumeric;
|
||||
import org.jruby.RubyObject;
|
||||
import org.jruby.RubyString;
|
||||
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
|
||||
import org.jruby.runtime.Block;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
|
||||
import org.jruby.exceptions.RaiseException;
|
||||
|
||||
import org.jruby.util.ByteList;
|
||||
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import javax.net.ssl.SSLEngineResult.*;
|
||||
import java.io.*;
|
||||
import java.security.*;
|
||||
import java.nio.*;
|
||||
|
||||
public class MiniSSL extends RubyObject {
|
||||
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
||||
return new MiniSSL(runtime, klass);
|
||||
}
|
||||
};
|
||||
|
||||
public static void createMiniSSL(Ruby runtime) {
|
||||
RubyModule mPuma = runtime.defineModule("Puma");
|
||||
RubyModule ssl = mPuma.defineModuleUnder("MiniSSL");
|
||||
|
||||
mPuma.defineClassUnder("SSLError",
|
||||
runtime.getClass("IOError"),
|
||||
runtime.getClass("IOError").getAllocator());
|
||||
|
||||
RubyClass eng = ssl.defineClassUnder("Engine",runtime.getObject(),ALLOCATOR);
|
||||
eng.defineAnnotatedMethods(MiniSSL.class);
|
||||
}
|
||||
|
||||
private Ruby runtime;
|
||||
private SSLContext sslc;
|
||||
|
||||
private SSLEngine engine;
|
||||
|
||||
private ByteBuffer peerAppData;
|
||||
private ByteBuffer peerNetData;
|
||||
private ByteBuffer netData;
|
||||
private ByteBuffer dummy;
|
||||
|
||||
public MiniSSL(Ruby runtime, RubyClass klass) {
|
||||
super(runtime, klass);
|
||||
|
||||
this.runtime = runtime;
|
||||
}
|
||||
|
||||
@JRubyMethod(meta = true)
|
||||
public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject key, IRubyObject cert) {
|
||||
RubyClass klass = (RubyClass) recv;
|
||||
IRubyObject newInstance = klass.newInstance(context,
|
||||
new IRubyObject[] { key, cert },
|
||||
Block.NULL_BLOCK);
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(IRubyObject key, IRubyObject cert)
|
||||
throws java.security.KeyStoreException,
|
||||
java.io.FileNotFoundException,
|
||||
java.io.IOException,
|
||||
java.io.FileNotFoundException,
|
||||
java.security.NoSuchAlgorithmException,
|
||||
java.security.KeyManagementException,
|
||||
java.security.cert.CertificateException,
|
||||
java.security.UnrecoverableKeyException
|
||||
{
|
||||
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
|
||||
char[] pass = "blahblah".toCharArray();
|
||||
|
||||
ks.load(new FileInputStream(key.convertToString().asJavaString()),
|
||||
pass);
|
||||
ts.load(new FileInputStream(cert.convertToString().asJavaString()),
|
||||
pass);
|
||||
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
kmf.init(ks, pass);
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
||||
tmf.init(ts);
|
||||
|
||||
SSLContext sslCtx = SSLContext.getInstance("TLS");
|
||||
|
||||
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
|
||||
sslc = sslCtx;
|
||||
|
||||
engine = sslc.createSSLEngine();
|
||||
engine.setUseClientMode(false);
|
||||
// engine.setNeedClientAuth(true);
|
||||
|
||||
SSLSession session = engine.getSession();
|
||||
peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
|
||||
peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
|
||||
netData = ByteBuffer.allocate(session.getPacketBufferSize());
|
||||
peerNetData.limit(0);
|
||||
peerAppData.limit(0);
|
||||
netData.limit(0);
|
||||
|
||||
peerNetData.clear();
|
||||
peerAppData.clear();
|
||||
netData.clear();
|
||||
|
||||
dummy = ByteBuffer.allocate(0);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject inject(IRubyObject arg) {
|
||||
byte[] bytes = arg.convertToString().getBytes();
|
||||
|
||||
peerNetData.limit(peerNetData.limit() + bytes.length);
|
||||
|
||||
log("capacity: " + peerNetData.capacity() + " limit: " + peerNetData.limit());
|
||||
|
||||
peerNetData.put(bytes);
|
||||
|
||||
log("netData: " + peerNetData.position() + "/" + peerAppData.limit());
|
||||
return this;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject read() throws javax.net.ssl.SSLException, Exception {
|
||||
peerAppData.clear();
|
||||
peerNetData.flip();
|
||||
SSLEngineResult res;
|
||||
|
||||
log("available read: " + peerNetData.position() + "/ " + peerNetData.limit());
|
||||
|
||||
if(!peerNetData.hasRemaining()) {
|
||||
return getRuntime().getNil();
|
||||
}
|
||||
|
||||
do {
|
||||
res = engine.unwrap(peerNetData, peerAppData);
|
||||
} while(res.getStatus() == SSLEngineResult.Status.OK &&
|
||||
res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&
|
||||
res.bytesProduced() == 0);
|
||||
|
||||
log("read: ", res);
|
||||
|
||||
if(peerNetData.hasRemaining()) {
|
||||
log("STILL HAD peerNetData!");
|
||||
}
|
||||
|
||||
peerNetData.position(0);
|
||||
peerNetData.limit(0);
|
||||
|
||||
HandshakeStatus hsStatus = runDelegatedTasks(res, engine);
|
||||
|
||||
if(res.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
|
||||
return getRuntime().getNil();
|
||||
}
|
||||
|
||||
if(hsStatus == HandshakeStatus.NEED_WRAP) {
|
||||
netData.clear();
|
||||
log("netData: " + netData.limit());
|
||||
engine.wrap(dummy, netData);
|
||||
return getRuntime().getNil();
|
||||
}
|
||||
|
||||
if(hsStatus == HandshakeStatus.NEED_UNWRAP) {
|
||||
return getRuntime().getNil();
|
||||
|
||||
// log("peerNet: " + peerNetData.position() + "/" + peerNetData.limit());
|
||||
// log("peerApp: " + peerAppData.position() + "/" + peerAppData.limit());
|
||||
|
||||
// peerNetData.compact();
|
||||
|
||||
// log("peerNet: " + peerNetData.position() + "/" + peerNetData.limit());
|
||||
// do {
|
||||
// res = engine.unwrap(peerNetData, peerAppData);
|
||||
// } while(res.getStatus() == SSLEngineResult.Status.OK &&
|
||||
// res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&
|
||||
// res.bytesProduced() == 0);
|
||||
// return getRuntime().getNil();
|
||||
}
|
||||
|
||||
// if(peerAppData.position() == 0 &&
|
||||
// res.getStatus() == SSLEngineResult.Status.OK &&
|
||||
// peerNetData.hasRemaining()) {
|
||||
// res = engine.unwrap(peerNetData, peerAppData);
|
||||
// }
|
||||
|
||||
byte[] bss = new byte[peerAppData.limit()];
|
||||
|
||||
peerAppData.get(bss);
|
||||
|
||||
RubyString str = getRuntime().newString("");
|
||||
str.setValue(new ByteList(bss));
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
private static HandshakeStatus runDelegatedTasks(SSLEngineResult result,
|
||||
SSLEngine engine) throws Exception {
|
||||
|
||||
HandshakeStatus hsStatus = result.getHandshakeStatus();
|
||||
|
||||
if(hsStatus == HandshakeStatus.NEED_TASK) {
|
||||
Runnable runnable;
|
||||
while ((runnable = engine.getDelegatedTask()) != null) {
|
||||
log("\trunning delegated task...");
|
||||
runnable.run();
|
||||
}
|
||||
hsStatus = engine.getHandshakeStatus();
|
||||
if (hsStatus == HandshakeStatus.NEED_TASK) {
|
||||
throw new Exception(
|
||||
"handshake shouldn't need additional tasks");
|
||||
}
|
||||
log("\tnew HandshakeStatus: " + hsStatus);
|
||||
}
|
||||
|
||||
return hsStatus;
|
||||
}
|
||||
|
||||
|
||||
private static void log(String str, SSLEngineResult result) {
|
||||
System.out.println("The format of the SSLEngineResult is: \n" +
|
||||
"\t\"getStatus() / getHandshakeStatus()\" +\n" +
|
||||
"\t\"bytesConsumed() / bytesProduced()\"\n");
|
||||
|
||||
HandshakeStatus hsStatus = result.getHandshakeStatus();
|
||||
log(str +
|
||||
result.getStatus() + "/" + hsStatus + ", " +
|
||||
result.bytesConsumed() + "/" + result.bytesProduced() +
|
||||
" bytes");
|
||||
if (hsStatus == HandshakeStatus.FINISHED) {
|
||||
log("\t...ready for application data");
|
||||
}
|
||||
}
|
||||
|
||||
private static void log(String str) {
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject write(IRubyObject arg) throws javax.net.ssl.SSLException {
|
||||
log("write from: " + netData.position());
|
||||
|
||||
byte[] bls = arg.convertToString().getBytes();
|
||||
ByteBuffer src = ByteBuffer.wrap(bls);
|
||||
|
||||
SSLEngineResult res = engine.wrap(src, netData);
|
||||
|
||||
return getRuntime().newFixnum(res.bytesConsumed());
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject extract() {
|
||||
netData.flip();
|
||||
|
||||
if(!netData.hasRemaining()) {
|
||||
return getRuntime().getNil();
|
||||
}
|
||||
|
||||
byte[] bss = new byte[netData.limit()];
|
||||
|
||||
netData.get(bss);
|
||||
netData.clear();
|
||||
|
||||
RubyString str = getRuntime().newString("");
|
||||
str.setValue(new ByteList(bss));
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
|
@ -457,6 +457,7 @@ VALUE HttpParser_body(VALUE self) {
|
|||
}
|
||||
|
||||
void Init_io_buffer(VALUE puma);
|
||||
void Init_mini_ssl(VALUE mod);
|
||||
|
||||
void Init_puma_http11()
|
||||
{
|
||||
|
@ -486,4 +487,5 @@ void Init_puma_http11()
|
|||
init_common_fields();
|
||||
|
||||
Init_io_buffer(mPuma);
|
||||
Init_mini_ssl(mPuma);
|
||||
}
|
||||
|
|
|
@ -99,22 +99,22 @@ module Puma
|
|||
@listeners << [str, io]
|
||||
when "ssl"
|
||||
params = Rack::Utils.parse_query uri.query
|
||||
require 'openssl'
|
||||
require 'puma/minissl'
|
||||
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx = MiniSSL::Context.new
|
||||
unless params['key']
|
||||
logger.error "Please specify the SSL key via 'key='"
|
||||
error "Please specify the SSL key via 'key='"
|
||||
end
|
||||
|
||||
ctx.key = OpenSSL::PKey::RSA.new File.read(params['key'])
|
||||
ctx.key = params['key']
|
||||
|
||||
unless params['cert']
|
||||
logger.error "Please specify the SSL cert via 'cert='"
|
||||
error "Please specify the SSL cert via 'cert='"
|
||||
end
|
||||
|
||||
ctx.cert = OpenSSL::X509::Certificate.new File.read(params['cert'])
|
||||
ctx.cert = params['cert']
|
||||
|
||||
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
ctx.verify_mode = MiniSSL::VERIFY_NONE
|
||||
|
||||
if fd = @inherited_fds.delete(str)
|
||||
logger.log "* Inherited #{str}"
|
||||
|
@ -181,6 +181,8 @@ module Puma
|
|||
|
||||
def add_ssl_listener(host, port, ctx,
|
||||
optimize_for_latency=true, backlog=1024)
|
||||
require 'puma/minissl'
|
||||
|
||||
s = TCPServer.new(host, port)
|
||||
if optimize_for_latency
|
||||
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
||||
|
@ -188,7 +190,7 @@ module Puma
|
|||
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
||||
s.listen backlog
|
||||
|
||||
ssl = OpenSSL::SSL::SSLServer.new(s, ctx)
|
||||
ssl = MiniSSL::Server.new s, ctx
|
||||
env = @proto_env.dup
|
||||
env[HTTPS_KEY] = HTTPS
|
||||
@envs[ssl] = env
|
||||
|
@ -199,7 +201,7 @@ module Puma
|
|||
|
||||
def inherited_ssl_listener(fd, ctx)
|
||||
s = TCPServer.for_fd(fd)
|
||||
@ios << OpenSSL::SSL::SSLServer.new(s, ctx)
|
||||
@ios << MiniSSL::Server.new(s, ctx)
|
||||
s
|
||||
end
|
||||
|
||||
|
|
|
@ -127,7 +127,11 @@ module Puma
|
|||
def try_to_finish
|
||||
return read_body unless @read_header
|
||||
|
||||
data = @io.readpartial(CHUNK_SIZE)
|
||||
begin
|
||||
data = @io.read_nonblock(CHUNK_SIZE)
|
||||
rescue Errno::EAGAIN
|
||||
return false
|
||||
end
|
||||
|
||||
if @buffer
|
||||
@buffer << data
|
||||
|
@ -207,7 +211,11 @@ module Puma
|
|||
want = remain
|
||||
end
|
||||
|
||||
chunk = @io.readpartial(want)
|
||||
begin
|
||||
chunk = @io.read_nonblock(want)
|
||||
rescue Errno::EAGAIN
|
||||
return false
|
||||
end
|
||||
|
||||
# No chunk means a closed socket
|
||||
unless chunk
|
||||
|
|
115
lib/puma/minissl.rb
Normal file
115
lib/puma/minissl.rb
Normal file
|
@ -0,0 +1,115 @@
|
|||
module Puma::MiniSSL
|
||||
class Socket
|
||||
def initialize(socket, engine)
|
||||
@socket = socket
|
||||
@engine = engine
|
||||
end
|
||||
|
||||
def to_io
|
||||
@socket
|
||||
end
|
||||
|
||||
def readpartial(size)
|
||||
while true
|
||||
output = @engine.read
|
||||
return output if output
|
||||
|
||||
data = @socket.readpartial(size)
|
||||
@engine.inject(data)
|
||||
output = @engine.read
|
||||
|
||||
return output if output
|
||||
|
||||
while neg_data = @engine.extract
|
||||
@socket.write neg_data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def read_nonblock(size)
|
||||
while true
|
||||
output = @engine.read
|
||||
return output if output
|
||||
|
||||
data = @socket.read_nonblock(size)
|
||||
|
||||
@engine.inject(data)
|
||||
output = @engine.read
|
||||
|
||||
return output if output
|
||||
|
||||
while neg_data = @engine.extract
|
||||
@socket.write neg_data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def write(data)
|
||||
need = data.size
|
||||
|
||||
while true
|
||||
wrote = @engine.write data
|
||||
enc = @engine.extract
|
||||
|
||||
if enc
|
||||
@socket.write enc
|
||||
end
|
||||
|
||||
need -= wrote
|
||||
|
||||
return data.size if need == 0
|
||||
|
||||
data = data[need..-1]
|
||||
end
|
||||
end
|
||||
|
||||
def flush
|
||||
@socket.flush
|
||||
end
|
||||
|
||||
def close
|
||||
@socket.close
|
||||
end
|
||||
|
||||
def peeraddr
|
||||
@socket.peeraddr
|
||||
end
|
||||
end
|
||||
|
||||
class Context
|
||||
attr_accessor :key, :cert, :verify_mode
|
||||
end
|
||||
|
||||
VERIFY_NONE = 0
|
||||
VERIFY_PEER = 1
|
||||
|
||||
#if defined?(JRUBY_VERSION)
|
||||
#class Engine
|
||||
#def self.server(key, cert)
|
||||
#new(key, cert)
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
class Server
|
||||
def initialize(socket, ctx)
|
||||
@socket = socket
|
||||
@ctx = ctx
|
||||
end
|
||||
|
||||
def to_io
|
||||
@socket
|
||||
end
|
||||
|
||||
def accept
|
||||
io = @socket.accept
|
||||
engine = Engine.server @ctx.key, @ctx.cert
|
||||
|
||||
Socket.new io, engine
|
||||
end
|
||||
|
||||
def close
|
||||
@socket.close
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@ require 'test/unit'
|
|||
require 'socket'
|
||||
require 'openssl'
|
||||
|
||||
require 'puma/minissl'
|
||||
require 'puma/server'
|
||||
|
||||
require 'net/https'
|
||||
|
@ -18,8 +19,13 @@ class TestPumaServer < Test::Unit::TestCase
|
|||
@events = Puma::Events.new STDOUT, STDERR
|
||||
@server = Puma::Server.new @app, @events
|
||||
|
||||
@ssl_key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
|
||||
@ssl_cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
|
||||
if defined?(JRUBY_VERSION)
|
||||
@ssl_key = File.expand_path "../../examples/puma/keystore.jks", __FILE__
|
||||
@ssl_cert = @ssl_key
|
||||
else
|
||||
@ssl_key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
|
||||
@ssl_cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
|
@ -27,13 +33,12 @@ class TestPumaServer < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_url_scheme_for_https
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx = Puma::MiniSSL::Context.new
|
||||
|
||||
ctx.key = OpenSSL::PKey::RSA.new File.read(@ssl_key)
|
||||
ctx.key = @ssl_key
|
||||
ctx.cert = @ssl_cert
|
||||
|
||||
ctx.cert = OpenSSL::X509::Certificate.new File.read(@ssl_cert)
|
||||
|
||||
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
ctx.verify_mode = Puma::MiniSSL::VERIFY_NONE
|
||||
|
||||
@server.add_ssl_listener @host, @port, ctx
|
||||
@server.run
|
||||
|
|
Loading…
Add table
Reference in a new issue