jubilee 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rbenv-version +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +54 -0
- data/Guardfile +24 -0
- data/README.md +61 -0
- data/Rakefile +94 -0
- data/VERSION +1 -0
- data/bin/jubilee +6 -0
- data/bin/jubilee_d +10 -0
- data/examples/jubilee/keystore.jks +0 -0
- data/examples/jubilee/server-keystore.jks +0 -0
- data/examples/ssl/ServerTest.java +19 -0
- data/examples/ssl/webroot/index.html +10 -0
- data/jars/netty-3.6.0.Beta1.jar +0 -0
- data/jars/vertx-core-1.3.0.final.jar +0 -0
- data/java/.idea/ant.xml +7 -0
- data/java/.idea/libraries/jruby.xml +9 -0
- data/java/.idea/libraries/netty_3_6_0_Beta1.xml +9 -0
- data/java/.idea/libraries/vertx_core_1_3_0_final.xml +9 -0
- data/java/src/jubilee/JubileeService.java +21 -0
- data/java/src/org/jruby/jubilee/Const.java +148 -0
- data/java/src/org/jruby/jubilee/RackApplication.java +78 -0
- data/java/src/org/jruby/jubilee/RackEnvironment.java +13 -0
- data/java/src/org/jruby/jubilee/RackErrors.java +44 -0
- data/java/src/org/jruby/jubilee/RackInput.java +62 -0
- data/java/src/org/jruby/jubilee/RackResponse.java +16 -0
- data/java/src/org/jruby/jubilee/Server.java +104 -0
- data/java/src/org/jruby/jubilee/deploy/Starter.java +26 -0
- data/java/src/org/jruby/jubilee/impl/DefaultRackEnvironment.java +98 -0
- data/java/src/org/jruby/jubilee/impl/NullIO.java +111 -0
- data/java/src/org/jruby/jubilee/impl/RubyIORackErrors.java +68 -0
- data/java/src/org/jruby/jubilee/impl/RubyIORackInput.java +164 -0
- data/lib/jubilee.rb +11 -0
- data/lib/jubilee/application.rb +13 -0
- data/lib/jubilee/cli.rb +74 -0
- data/lib/jubilee/configuration.rb +52 -0
- data/lib/jubilee/const.rb +39 -0
- data/lib/jubilee/jubilee.jar +0 -0
- data/lib/jubilee/response.rb +64 -0
- data/lib/jubilee/server.rb +16 -0
- data/lib/rack/handler/jubilee.rb +43 -0
- data/test/.rbenv-version +1 -0
- data/test/config/app.rb +5 -0
- data/test/jubilee/test_cli.rb +11 -0
- data/test/jubilee/test_config.rb +14 -0
- data/test/jubilee/test_persistent.rb +238 -0
- data/test/jubilee/test_rack_server.rb +116 -0
- data/test/jubilee/test_server.rb +68 -0
- data/test/sinatra_app/app.rb +31 -0
- data/test/sinatra_app/config.ru +6 -0
- data/test/sinatra_app/public/test.html +10 -0
- data/test/sinatra_app/unicorn.conf.rb +29 -0
- data/test/test_helper.rb +21 -0
- metadata +160 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
package org.jruby.jubilee;
|
2
|
+
|
3
|
+
import org.jruby.Ruby;
|
4
|
+
import org.jruby.javasupport.JavaEmbedUtils;
|
5
|
+
import org.jruby.jubilee.impl.DefaultRackEnvironment;
|
6
|
+
import org.jruby.jubilee.impl.RubyIORackInput;
|
7
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
8
|
+
import org.vertx.java.core.Handler;
|
9
|
+
import org.vertx.java.core.SimpleHandler;
|
10
|
+
import org.vertx.java.core.Vertx;
|
11
|
+
import org.vertx.java.core.buffer.Buffer;
|
12
|
+
import org.vertx.java.core.http.HttpServerRequest;
|
13
|
+
|
14
|
+
import java.util.concurrent.*;
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Created with IntelliJ IDEA.
|
18
|
+
* User: isaiah
|
19
|
+
* Date: 11/29/12
|
20
|
+
* Time: 5:40 PM
|
21
|
+
*/
|
22
|
+
public class RackApplication {
|
23
|
+
private IRubyObject app;
|
24
|
+
private boolean ssl;
|
25
|
+
private Buffer bodyBuf;
|
26
|
+
private Vertx vertx;
|
27
|
+
|
28
|
+
private ExecutorService exec;
|
29
|
+
|
30
|
+
public RackApplication(Vertx vertx, IRubyObject app, boolean ssl) {
|
31
|
+
this.app = app;
|
32
|
+
this.ssl = ssl;
|
33
|
+
bodyBuf = new Buffer(0);
|
34
|
+
this.vertx = vertx;
|
35
|
+
exec = Executors.newCachedThreadPool();
|
36
|
+
}
|
37
|
+
|
38
|
+
public void call(final HttpServerRequest request) {
|
39
|
+
final Ruby runtime = app.getRuntime();
|
40
|
+
final CountDownLatch bodyLatch = new CountDownLatch(1);
|
41
|
+
request.dataHandler(new Handler<Buffer>() {
|
42
|
+
@Override
|
43
|
+
public void handle(Buffer buffer) {
|
44
|
+
bodyBuf.appendBuffer(buffer);
|
45
|
+
}
|
46
|
+
});
|
47
|
+
// TODO optimize by use NullIO when there is no body here.
|
48
|
+
Runnable task = new Runnable() {
|
49
|
+
@Override
|
50
|
+
public void run() {
|
51
|
+
RackInput input = new RubyIORackInput(runtime, bodyBuf, bodyLatch);
|
52
|
+
RackEnvironment env = new DefaultRackEnvironment(runtime, request, input, ssl);
|
53
|
+
IRubyObject result = app.callMethod(runtime.getCurrentContext(), "call", env.getEnv());
|
54
|
+
final RackResponse response = (RackResponse) JavaEmbedUtils.rubyToJava(runtime, result, RackResponse.class);
|
55
|
+
vertx.runOnLoop(new SimpleHandler() {
|
56
|
+
@Override
|
57
|
+
public void handle() {
|
58
|
+
response.respond(request.response);
|
59
|
+
}
|
60
|
+
});
|
61
|
+
}
|
62
|
+
};
|
63
|
+
exec.execute(task);
|
64
|
+
request.endHandler(new SimpleHandler() {
|
65
|
+
@Override
|
66
|
+
protected void handle() {
|
67
|
+
bodyLatch.countDown();
|
68
|
+
}
|
69
|
+
});
|
70
|
+
}
|
71
|
+
|
72
|
+
public void shutdown(boolean force) {
|
73
|
+
if (force)
|
74
|
+
exec.shutdownNow();
|
75
|
+
else
|
76
|
+
exec.shutdown();
|
77
|
+
}
|
78
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
package org.jruby.jubilee;
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Created with IntelliJ IDEA.
|
5
|
+
* User: isaiah
|
6
|
+
* Date: 11/26/12
|
7
|
+
* Time: 12:01 PM
|
8
|
+
*/
|
9
|
+
import org.jruby.runtime.ThreadContext;
|
10
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
11
|
+
|
12
|
+
public interface RackErrors {
|
13
|
+
/**
|
14
|
+
* puts must be called with a single argument that responds to to_s.
|
15
|
+
*
|
16
|
+
* @param context ruby context
|
17
|
+
* @param arg must respond to to_s
|
18
|
+
* @return nil
|
19
|
+
*/
|
20
|
+
IRubyObject puts( ThreadContext context, IRubyObject arg );
|
21
|
+
|
22
|
+
/**
|
23
|
+
* write must be called with a single argument that is a String.
|
24
|
+
*
|
25
|
+
* @param context ruby context
|
26
|
+
* @param string a Ruby String
|
27
|
+
* @return nil
|
28
|
+
*/
|
29
|
+
IRubyObject write( ThreadContext context, IRubyObject string );
|
30
|
+
|
31
|
+
/**
|
32
|
+
* flush must be called without arguments and must be called in order to make the error appear for sure.
|
33
|
+
*
|
34
|
+
* @return nil
|
35
|
+
*/
|
36
|
+
IRubyObject flush();
|
37
|
+
|
38
|
+
/**
|
39
|
+
* close must never be called on the error stream.
|
40
|
+
*
|
41
|
+
* @return nil
|
42
|
+
*/
|
43
|
+
IRubyObject close();
|
44
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
package org.jruby.jubilee;
|
2
|
+
|
3
|
+
import org.jruby.runtime.Block;
|
4
|
+
import org.jruby.runtime.ThreadContext;
|
5
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
6
|
+
|
7
|
+
/**
|
8
|
+
* @author isaiah
|
9
|
+
* @since Nov 26, 2012
|
10
|
+
*
|
11
|
+
* Specification for Rack input, translated to a Java interface.
|
12
|
+
* @author nicksieger
|
13
|
+
*/
|
14
|
+
public interface RackInput
|
15
|
+
{
|
16
|
+
/**
|
17
|
+
* gets must be called without arguments and return a string, or nil on EOF.
|
18
|
+
* @param context it's a JRuby thing
|
19
|
+
* @return a string, or nil on EOF
|
20
|
+
*/
|
21
|
+
IRubyObject gets( ThreadContext context );
|
22
|
+
|
23
|
+
/**
|
24
|
+
* read behaves like IO#read. Its signature is read([length, [buffer]]). If given,
|
25
|
+
* length must be an non-negative Integer (>= 0) or nil, and buffer must be a
|
26
|
+
* String and may not be nil. If length is given and not nil, then this method
|
27
|
+
* reads at most length bytes from the input stream. If length is not given or
|
28
|
+
* nil, then this method reads all data until EOF. When EOF is reached, this
|
29
|
+
* method returns nil if length is given and not nil, or "" if length is not
|
30
|
+
* given or is nil. If buffer is given, then the read data will be placed into
|
31
|
+
* buffer instead of a newly created String object.
|
32
|
+
* @param context it's a JRuby thing
|
33
|
+
* @param args [length, [buffer]]
|
34
|
+
* @return nil if length is given and not nil, or "" if length is not given or nil
|
35
|
+
*/
|
36
|
+
IRubyObject read( ThreadContext context, IRubyObject[] args );
|
37
|
+
|
38
|
+
/**
|
39
|
+
* each must be called without arguments and only yield Strings.
|
40
|
+
* @param context it's a JRuby thing
|
41
|
+
* @param block that receives yield of Strings
|
42
|
+
* @return pretty much nil
|
43
|
+
*/
|
44
|
+
public IRubyObject each( ThreadContext context, Block block );
|
45
|
+
|
46
|
+
/**
|
47
|
+
* rewind must be called without arguments. It rewinds the input stream back
|
48
|
+
* to the beginning. It must not raise Errno::ESPIPE: that is, it may not be
|
49
|
+
* a pipe or a socket. Therefore, handler developers must buffer the input
|
50
|
+
* data into some rewindable object if the underlying input stream is not rewindable.
|
51
|
+
* @param context it's a JRuby thing
|
52
|
+
* @return pretty much nil
|
53
|
+
*/
|
54
|
+
public IRubyObject rewind( ThreadContext context );
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Close the input. Exposed only to the Java side because the Rack spec says
|
58
|
+
* that application code must not call close, so we don't expose a close method to Ruby.
|
59
|
+
*/
|
60
|
+
public IRubyObject close(ThreadContext context);
|
61
|
+
}
|
62
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
package org.jruby.jubilee;
|
2
|
+
|
3
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
4
|
+
import org.vertx.java.core.http.HttpServerResponse;
|
5
|
+
|
6
|
+
import java.util.Map;
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Created with IntelliJ IDEA.
|
10
|
+
* User: isaiah
|
11
|
+
* Date: 11/29/12
|
12
|
+
* Time: 5:39 PM
|
13
|
+
*/
|
14
|
+
public interface RackResponse {
|
15
|
+
public void respond(HttpServerResponse response);
|
16
|
+
}
|
@@ -0,0 +1,104 @@
|
|
1
|
+
package org.jruby.jubilee;
|
2
|
+
|
3
|
+
import org.vertx.java.core.*;
|
4
|
+
import org.vertx.java.core.http.*;
|
5
|
+
|
6
|
+
import org.jruby.*;
|
7
|
+
import org.jruby.runtime.*;
|
8
|
+
import org.jruby.runtime.builtin.*;
|
9
|
+
import org.jruby.anno.JRubyMethod;
|
10
|
+
|
11
|
+
public class Server extends RubyObject {
|
12
|
+
final private Vertx vertx;
|
13
|
+
final private HttpServer httpServer;
|
14
|
+
private RackApplication app;
|
15
|
+
private boolean running;
|
16
|
+
private boolean ssl = false;
|
17
|
+
private String keyStorePath;
|
18
|
+
private String keyStorePassword;
|
19
|
+
private int port;
|
20
|
+
|
21
|
+
public static void createServerClass(Ruby runtime) {
|
22
|
+
RubyModule mJubilee = runtime.defineModule("Jubilee");
|
23
|
+
RubyClass serverClass = mJubilee.defineClassUnder("VertxServer", runtime.getObject(), ALLOCATOR);
|
24
|
+
serverClass.defineAnnotatedMethods(Server.class);
|
25
|
+
}
|
26
|
+
|
27
|
+
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
28
|
+
public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
|
29
|
+
return new Server(ruby, rubyClass);
|
30
|
+
}
|
31
|
+
};
|
32
|
+
|
33
|
+
public Server(Ruby ruby, RubyClass rubyClass) {
|
34
|
+
super(ruby, rubyClass);
|
35
|
+
vertx = Vertx.newVertx();
|
36
|
+
httpServer = vertx.createHttpServer();
|
37
|
+
}
|
38
|
+
|
39
|
+
@JRubyMethod(name = "initialize", required = 2, optional = 3)
|
40
|
+
public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block block) {
|
41
|
+
this.port = RubyInteger.num2int(args[1]);
|
42
|
+
if (args.length > 2) {
|
43
|
+
this.ssl = args[2].isTrue();
|
44
|
+
}
|
45
|
+
this.app = new RackApplication(vertx, args[0], this.ssl);
|
46
|
+
if (args.length > 3)
|
47
|
+
this.keyStorePath = args[3].toString();
|
48
|
+
if (args.length > 4)
|
49
|
+
this.keyStorePassword = args[4].toString();
|
50
|
+
running = false;
|
51
|
+
return this;
|
52
|
+
}
|
53
|
+
|
54
|
+
@JRubyMethod(name = "start", optional = 1)
|
55
|
+
public IRubyObject start(final ThreadContext context, final IRubyObject[] args, final Block block) {
|
56
|
+
this.running = true;
|
57
|
+
httpServer.setAcceptBacklog(10000);
|
58
|
+
httpServer.requestHandler(new Handler<HttpServerRequest>() {
|
59
|
+
public void handle(final HttpServerRequest req) {
|
60
|
+
app.call(req);
|
61
|
+
}
|
62
|
+
});
|
63
|
+
if (ssl) httpServer.setSSL(true).setKeyStorePath(this.keyStorePath)
|
64
|
+
.setKeyStorePassword(this.keyStorePassword);
|
65
|
+
httpServer.listen(this.port);
|
66
|
+
return this;
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Set timeout for keep alive connection
|
71
|
+
* @param context
|
72
|
+
* @param timeout (in TimeUnit.SECONDS)
|
73
|
+
* @return this
|
74
|
+
*/
|
75
|
+
@JRubyMethod(name = "persistent_timeout=")
|
76
|
+
public IRubyObject setPersistentTimeout(final ThreadContext context, final IRubyObject timeout) {
|
77
|
+
httpServer.setPersistentTimeout(RubyInteger.fix2long(timeout) * 1000);
|
78
|
+
return this;
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Stop the HttpServer
|
83
|
+
* @param context
|
84
|
+
* @param args if shutdown abruptly
|
85
|
+
* @param block callback on close
|
86
|
+
* @return
|
87
|
+
*/
|
88
|
+
@JRubyMethod(name = {"stop", "close"}, optional = 1)
|
89
|
+
public IRubyObject close(ThreadContext context, IRubyObject[] args, Block block) {
|
90
|
+
if (running) {
|
91
|
+
if (args.length == 1)
|
92
|
+
app.shutdown(args[0].isTrue());
|
93
|
+
else
|
94
|
+
app.shutdown(false);
|
95
|
+
|
96
|
+
this.running = false;
|
97
|
+
httpServer.close();
|
98
|
+
if (block.isGiven()) block.yieldSpecific(context);
|
99
|
+
} else {
|
100
|
+
getRuntime().getOutputStream().println("jubilee server not running?");
|
101
|
+
}
|
102
|
+
return getRuntime().getNil();
|
103
|
+
}
|
104
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
package org.jruby.jubilee.deploy;
|
2
|
+
|
3
|
+
import java.util.concurrent.CountDownLatch;
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Created with IntelliJ IDEA.
|
7
|
+
* User: isaiah
|
8
|
+
* Date: 12/3/12
|
9
|
+
* Time: 7:37 PM
|
10
|
+
*/
|
11
|
+
public class Starter {
|
12
|
+
private CountDownLatch stopLatch = new CountDownLatch(1);
|
13
|
+
|
14
|
+
public void block() {
|
15
|
+
while(true) {
|
16
|
+
try {
|
17
|
+
stopLatch.await();
|
18
|
+
break;
|
19
|
+
} catch (InterruptedException ignore) {}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
public void unblock() {
|
24
|
+
stopLatch.countDown();
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,98 @@
|
|
1
|
+
package org.jruby.jubilee.impl;
|
2
|
+
|
3
|
+
import org.jruby.RubyArray;
|
4
|
+
import org.jruby.RubyString;
|
5
|
+
import org.jruby.jubilee.RackEnvironment;
|
6
|
+
import org.jruby.jubilee.Const;
|
7
|
+
import org.jruby.Ruby;
|
8
|
+
import org.jruby.RubyHash;
|
9
|
+
import org.jruby.jubilee.RackInput;
|
10
|
+
import org.vertx.java.core.http.HttpServerRequest;
|
11
|
+
|
12
|
+
import java.util.Map;
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Created with IntelliJ IDEA.
|
16
|
+
* User: isaiah
|
17
|
+
* Date: 11/26/12
|
18
|
+
* Time: 11:40 AM
|
19
|
+
*/
|
20
|
+
public class DefaultRackEnvironment implements RackEnvironment {
|
21
|
+
private RubyHash env;
|
22
|
+
private Map<String, String> headers;
|
23
|
+
private Ruby runtime;
|
24
|
+
|
25
|
+
public DefaultRackEnvironment(final Ruby runtime, final HttpServerRequest request, RackInput input, boolean isSSL) {
|
26
|
+
|
27
|
+
this.runtime = runtime;
|
28
|
+
// DEFAULT
|
29
|
+
env = RubyHash.newHash(runtime);
|
30
|
+
env.put(Const.RACK_VERSION, Const.RackVersion(runtime));
|
31
|
+
env.put(Const.RACK_ERRORS, new RubyIORackErrors(runtime));
|
32
|
+
env.put(Const.RACK_MULTITHREAD, true);
|
33
|
+
env.put(Const.RACK_MULTIPROCESS, false);
|
34
|
+
env.put(Const.RACK_RUNONCE, true);
|
35
|
+
if (isSSL)
|
36
|
+
env.put(Const.URL_SCHEME, Const.HTTPS);
|
37
|
+
else
|
38
|
+
env.put(Const.URL_SCHEME, Const.HTTP);
|
39
|
+
env.put(Const.SCRIPT_NAME, "");
|
40
|
+
|
41
|
+
env.put(Const.SERVER_PROTOCOL, Const.HTTP_11);
|
42
|
+
env.put(Const.SERVER_SOFTWARE, Const.JUBILEE_VERSION);
|
43
|
+
env.put(Const.GATEWAY_INTERFACE, Const.CGI_VER);
|
44
|
+
|
45
|
+
// Parse request headers
|
46
|
+
headers = request.headers();
|
47
|
+
String host;
|
48
|
+
if ((host = headers.get(Const.Vertx.HOST)) != null) {
|
49
|
+
int colon = host.indexOf(":");
|
50
|
+
if (colon > 0) {
|
51
|
+
env.put(Const.SERVER_NAME, host.substring(0, colon));
|
52
|
+
env.put(Const.SERVER_PORT, host.substring(colon + 1));
|
53
|
+
} else {
|
54
|
+
env.put(Const.SERVER_NAME, host);
|
55
|
+
env.put(Const.SERVER_PORT, Const.PORT_80);
|
56
|
+
}
|
57
|
+
|
58
|
+
} else {
|
59
|
+
env.put(Const.SERVER_NAME, Const.LOCALHOST);
|
60
|
+
env.put(Const.SERVER_PORT, Const.PORT_80);
|
61
|
+
}
|
62
|
+
|
63
|
+
env.put(Const.RACK_INPUT, input);
|
64
|
+
env.put(Const.REQUEST_METHOD, request.method);
|
65
|
+
env.put(Const.REQUEST_PATH, request.path);
|
66
|
+
env.put(Const.REQUEST_URI, request.uri);
|
67
|
+
env.put(Const.QUERY_STRING, orEmpty(request.query));
|
68
|
+
env.put(Const.REMOTE_ADDR, request.remoteAddr);
|
69
|
+
env.put(Const.HTTP_HOST, host);
|
70
|
+
env.put(Const.HTTP_COOKIE, orEmpty(headers.get(Const.Vertx.COOKIE)));
|
71
|
+
env.put(Const.HTTP_USER_AGENT, headers.get(Const.Vertx.USER_AGENT));
|
72
|
+
env.put(Const.HTTP_ACCEPT, headers.get(Const.Vertx.ACCEPT));
|
73
|
+
env.put(Const.HTTP_ACCEPT_LANGUAGE, orEmpty(headers.get(Const.Vertx.ACCEPT_LANGUAGE)));
|
74
|
+
env.put(Const.HTTP_ACCEPT_ENCODING, orEmpty(headers.get(Const.Vertx.ACCEPT_ENCODING)));
|
75
|
+
env.put(Const.HTTP_CONNECTION, orEmpty(headers.get(Const.Vertx.CONNECTION)));
|
76
|
+
env.put(Const.HTTP_CONTENT_TYPE, orEmpty(headers.get(Const.Vertx.CONTENT_TYPE)));
|
77
|
+
String contentLength;
|
78
|
+
if ((contentLength = headers.get(Const.Vertx.CONTENT_LENGTH)) != null)
|
79
|
+
env.put(Const.HTTP_CONTENT_LENGTH, contentLength);
|
80
|
+
env.put(Const.PATH_INFO, request.path);
|
81
|
+
|
82
|
+
// Additional headers
|
83
|
+
for (Map.Entry<String, String> var : Const.ADDITIONAL_HEADERS.entrySet())
|
84
|
+
setRackHeader(var.getKey(), var.getValue());
|
85
|
+
}
|
86
|
+
|
87
|
+
public RubyHash getEnv() {
|
88
|
+
return env;
|
89
|
+
}
|
90
|
+
|
91
|
+
private RubyString orEmpty(String jString) {
|
92
|
+
return jString == null ? RubyString.newEmptyString(runtime) : RubyString.newString(runtime, jString);
|
93
|
+
}
|
94
|
+
|
95
|
+
private void setRackHeader(String vertxHeader, String rackHeader) {
|
96
|
+
if (headers.containsKey(vertxHeader)) env.put(rackHeader, headers.get(vertxHeader));
|
97
|
+
}
|
98
|
+
}
|