jubilee 1.1.0-java
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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CHANGELOG +32 -0
- data/Gemfile +35 -0
- data/Gemfile.lock +244 -0
- data/Guardfile +24 -0
- data/KNOWN_ISSUES +6 -0
- data/LICENSE.txt +20 -0
- data/README.md +147 -0
- data/ROADMAP +5 -0
- data/Rakefile +98 -0
- data/bin/jubilee +6 -0
- data/bin/jubilee_d +13 -0
- data/examples/chatapp/Gemfile +5 -0
- data/examples/chatapp/Gemfile.lock +27 -0
- data/examples/chatapp/README.md +17 -0
- data/examples/chatapp/app.rb +57 -0
- data/examples/chatapp/config.ru +3 -0
- data/examples/chatapp/public/assets/javascripts/application.js +67 -0
- data/examples/chatapp/public/assets/javascripts/jquery.js +5 -0
- data/examples/chatapp/public/assets/javascripts/sockjs-0.3.4.min.js +27 -0
- data/examples/chatapp/public/assets/javascripts/vertxbus.js +216 -0
- data/examples/chatapp/public/assets/stylesheets/application.css +29 -0
- data/examples/client/sockjs-0.3.4.min.js +27 -0
- data/examples/client/vertxbus.js +216 -0
- data/examples/jubilee.conf.rb +12 -0
- data/examples/jubilee/keystore.jks +0 -0
- data/examples/jubilee/server-keystore.jks +0 -0
- data/jars/hazelcast-2.6.3.jar +0 -0
- data/jars/jackson-annotations-2.2.2.jar +0 -0
- data/jars/jackson-core-2.2.2.jar +0 -0
- data/jars/jackson-databind-2.2.2.jar +0 -0
- data/jars/netty-all-4.0.13.Final.jar +0 -0
- data/jars/vertx-core-2.1M3-SNAPSHOT.jar +0 -0
- data/jars/vertx-hazelcast-2.1M3-SNAPSHOT.jar +0 -0
- data/java/src/jubilee/JubileeService.java +20 -0
- data/java/src/org/jruby/jubilee/Const.java +32 -0
- data/java/src/org/jruby/jubilee/RackApplication.java +103 -0
- data/java/src/org/jruby/jubilee/RackEnvironment.java +150 -0
- data/java/src/org/jruby/jubilee/RackEnvironmentHash.java +449 -0
- data/java/src/org/jruby/jubilee/RackInput.java +62 -0
- data/java/src/org/jruby/jubilee/RackResponse.java +11 -0
- data/java/src/org/jruby/jubilee/RubyHttpServerResponse.java +88 -0
- data/java/src/org/jruby/jubilee/RubyServer.java +171 -0
- data/java/src/org/jruby/jubilee/deploy/Starter.java +26 -0
- data/java/src/org/jruby/jubilee/impl/RubyIORackInput.java +201 -0
- data/java/src/org/jruby/jubilee/impl/RubyNullIO.java +107 -0
- data/java/src/org/jruby/jubilee/utils/RubyHelper.java +37 -0
- data/java/src/org/jruby/jubilee/vertx/JubileeVertx.java +38 -0
- data/jubilee.gemspec +201 -0
- data/lib/jubilee.rb +17 -0
- data/lib/jubilee/application.rb +13 -0
- data/lib/jubilee/cli.rb +127 -0
- data/lib/jubilee/configuration.rb +177 -0
- data/lib/jubilee/const.rb +40 -0
- data/lib/jubilee/jubilee.jar +0 -0
- data/lib/jubilee/response.rb +69 -0
- data/lib/jubilee/server.rb +12 -0
- data/lib/jubilee/version.rb +10 -0
- data/lib/rack/chunked.rb +38 -0
- data/lib/rack/handler/jubilee.rb +44 -0
- data/lib/vertx.rb +26 -0
- data/lib/vertx/README.md +7 -0
- data/lib/vertx/buffer.rb +251 -0
- data/lib/vertx/event_bus.rb +206 -0
- data/lib/vertx/shared_data.rb +214 -0
- data/spec/apps/rack/basic/config.ru +50 -0
- data/spec/apps/rails4/basic/.gitignore +16 -0
- data/spec/apps/rails4/basic/Gemfile +41 -0
- data/spec/apps/rails4/basic/Gemfile.lock +127 -0
- data/spec/apps/rails4/basic/README.rdoc +28 -0
- data/spec/apps/rails4/basic/Rakefile +6 -0
- data/spec/apps/rails4/basic/app/assets/images/.keep +0 -0
- data/spec/apps/rails4/basic/app/assets/images/rails.png +0 -0
- data/spec/apps/rails4/basic/app/assets/javascripts/application.js +12 -0
- data/spec/apps/rails4/basic/app/assets/stylesheets/application.css +13 -0
- data/spec/apps/rails4/basic/app/controllers/application_controller.rb +5 -0
- data/spec/apps/rails4/basic/app/controllers/concerns/.keep +0 -0
- data/spec/apps/rails4/basic/app/controllers/reloader_controller.rb +11 -0
- data/spec/apps/rails4/basic/app/controllers/reloader_controller.rb.erb +11 -0
- data/spec/apps/rails4/basic/app/controllers/root_controller.rb +14 -0
- data/spec/apps/rails4/basic/app/helpers/application_helper.rb +2 -0
- data/spec/apps/rails4/basic/app/mailers/.keep +0 -0
- data/spec/apps/rails4/basic/app/models/.keep +0 -0
- data/spec/apps/rails4/basic/app/models/concerns/.keep +0 -0
- data/spec/apps/rails4/basic/app/views/layouts/application.html.erb +14 -0
- data/spec/apps/rails4/basic/app/views/reloader/index.html.erb +1 -0
- data/spec/apps/rails4/basic/app/views/root/index.html.erb +8 -0
- data/spec/apps/rails4/basic/app/views/root/streaming.html.erb +6 -0
- data/spec/apps/rails4/basic/bin/bundle +3 -0
- data/spec/apps/rails4/basic/bin/rails +4 -0
- data/spec/apps/rails4/basic/bin/rake +4 -0
- data/spec/apps/rails4/basic/config.ru +4 -0
- data/spec/apps/rails4/basic/config/application.rb +23 -0
- data/spec/apps/rails4/basic/config/boot.rb +4 -0
- data/spec/apps/rails4/basic/config/database.yml +20 -0
- data/spec/apps/rails4/basic/config/environment.rb +5 -0
- data/spec/apps/rails4/basic/config/environments/development.rb +29 -0
- data/spec/apps/rails4/basic/config/environments/production.rb +80 -0
- data/spec/apps/rails4/basic/config/environments/test.rb +36 -0
- data/spec/apps/rails4/basic/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/apps/rails4/basic/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/apps/rails4/basic/config/initializers/inflections.rb +16 -0
- data/spec/apps/rails4/basic/config/initializers/mime_types.rb +5 -0
- data/spec/apps/rails4/basic/config/initializers/secret_token.rb +12 -0
- data/spec/apps/rails4/basic/config/initializers/session_store.rb +2 -0
- data/spec/apps/rails4/basic/config/initializers/wrap_parameters.rb +14 -0
- data/spec/apps/rails4/basic/config/locales/en.yml +23 -0
- data/spec/apps/rails4/basic/config/routes.rb +5 -0
- data/spec/apps/rails4/basic/db/seeds.rb +7 -0
- data/spec/apps/rails4/basic/lib/assets/.keep +0 -0
- data/spec/apps/rails4/basic/lib/tasks/.keep +0 -0
- data/spec/apps/rails4/basic/public/404.html +58 -0
- data/spec/apps/rails4/basic/public/422.html +58 -0
- data/spec/apps/rails4/basic/public/500.html +57 -0
- data/spec/apps/rails4/basic/public/favicon.ico +0 -0
- data/spec/apps/rails4/basic/public/robots.txt +5 -0
- data/spec/apps/rails4/basic/public/some_page.html +7 -0
- data/spec/apps/rails4/basic/test/controllers/.keep +0 -0
- data/spec/apps/rails4/basic/test/fixtures/.keep +0 -0
- data/spec/apps/rails4/basic/test/helpers/.keep +0 -0
- data/spec/apps/rails4/basic/test/integration/.keep +0 -0
- data/spec/apps/rails4/basic/test/mailers/.keep +0 -0
- data/spec/apps/rails4/basic/test/models/.keep +0 -0
- data/spec/apps/rails4/basic/test/test_helper.rb +15 -0
- data/spec/apps/rails4/basic/vendor/assets/javascripts/.keep +0 -0
- data/spec/apps/rails4/basic/vendor/assets/stylesheets/.keep +0 -0
- data/spec/apps/sinatra/basic/Gemfile +4 -0
- data/spec/apps/sinatra/basic/Gemfile.lock +20 -0
- data/spec/apps/sinatra/basic/basic.rb +27 -0
- data/spec/apps/sinatra/basic/config.ru +7 -0
- data/spec/apps/sinatra/basic/public/some_page.html +7 -0
- data/spec/apps/sinatra/basic/views/index.erb +4 -0
- data/spec/apps/sinatra/basic/views/posted.haml +2 -0
- data/spec/apps/sinatra/basic/views/poster.haml +4 -0
- data/spec/apps/sinatra/basic/views/request_mapping.haml +4 -0
- data/spec/integration/basic_rack_spec.rb +89 -0
- data/spec/integration/basic_rails4_spec.rb +64 -0
- data/spec/integration/basic_sinatra_spec.rb +80 -0
- data/spec/spec_helper.rb +13 -0
- data/test/.ruby-version +1 -0
- data/test/config/app.rb +5 -0
- data/test/jubilee/test_cli.rb +11 -0
- data/test/jubilee/test_configuration.rb +31 -0
- data/test/jubilee/test_rack_server.rb +137 -0
- data/test/jubilee/test_response.rb +272 -0
- data/test/jubilee/test_server.rb +72 -0
- data/test/jubilee/test_upload.rb +301 -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 +93 -0
- metadata +242 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
package org.jruby.jubilee;
|
|
2
|
+
|
|
3
|
+
import io.netty.handler.codec.http.HttpHeaders;
|
|
4
|
+
import org.jruby.Ruby;
|
|
5
|
+
import org.jruby.RubyArray;
|
|
6
|
+
import org.jruby.RubyFixnum;
|
|
7
|
+
import org.jruby.RubyHash;
|
|
8
|
+
import org.jruby.RubyIO;
|
|
9
|
+
import org.jruby.RubyString;
|
|
10
|
+
|
|
11
|
+
import org.jruby.jubilee.utils.RubyHelper;
|
|
12
|
+
import org.vertx.java.core.MultiMap;
|
|
13
|
+
import org.vertx.java.core.http.HttpServerRequest;
|
|
14
|
+
import org.vertx.java.core.http.HttpVersion;
|
|
15
|
+
|
|
16
|
+
import java.io.IOException;
|
|
17
|
+
import java.net.InetSocketAddress;
|
|
18
|
+
import java.util.HashMap;
|
|
19
|
+
import java.util.Map;
|
|
20
|
+
|
|
21
|
+
public class RackEnvironment {
|
|
22
|
+
|
|
23
|
+
// When adding a key to the enum be sure to add its RubyString equivalent
|
|
24
|
+
// to populateRackKeyMap below
|
|
25
|
+
static enum RACK_KEY {
|
|
26
|
+
RACK_INPUT, RACK_ERRORS, REQUEST_METHOD, SCRIPT_NAME,
|
|
27
|
+
PATH_INFO, QUERY_STRING, SERVER_NAME, SERVER_PORT,
|
|
28
|
+
CONTENT_TYPE, REQUEST_URI, REMOTE_ADDR, URL_SCHEME,
|
|
29
|
+
VERSION, MULTITHREAD, MULTIPROCESS, RUN_ONCE, CONTENT_LENGTH,
|
|
30
|
+
HTTPS, HTTP_VERSION
|
|
31
|
+
}
|
|
32
|
+
static final int NUM_RACK_KEYS = RACK_KEY.values().length;
|
|
33
|
+
|
|
34
|
+
public RackEnvironment(final Ruby runtime) throws IOException {
|
|
35
|
+
this.runtime = runtime;
|
|
36
|
+
rackVersion = RubyArray.newArray(runtime, RubyFixnum.one(runtime), RubyFixnum.four(runtime));
|
|
37
|
+
errors = new RubyIO(runtime, runtime.getErr());
|
|
38
|
+
errors.setAutoclose(false);
|
|
39
|
+
|
|
40
|
+
populateRackKeyMap();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private void populateRackKeyMap() {
|
|
44
|
+
putRack("rack.input", RACK_KEY.RACK_INPUT);
|
|
45
|
+
putRack("rack.errors", RACK_KEY.RACK_ERRORS);
|
|
46
|
+
putRack("REQUEST_METHOD", RACK_KEY.REQUEST_METHOD);
|
|
47
|
+
putRack("SCRIPT_NAME", RACK_KEY.SCRIPT_NAME);
|
|
48
|
+
putRack("PATH_INFO", RACK_KEY.PATH_INFO);
|
|
49
|
+
putRack("QUERY_STRING", RACK_KEY.QUERY_STRING);
|
|
50
|
+
putRack("SERVER_NAME", RACK_KEY.SERVER_NAME);
|
|
51
|
+
putRack("SERVER_PORT", RACK_KEY.SERVER_PORT);
|
|
52
|
+
putRack("HTTP_VERSION", RACK_KEY.HTTP_VERSION);
|
|
53
|
+
putRack("CONTENT_TYPE", RACK_KEY.CONTENT_TYPE);
|
|
54
|
+
putRack("REQUEST_URI", RACK_KEY.REQUEST_URI);
|
|
55
|
+
putRack("REMOTE_ADDR", RACK_KEY.REMOTE_ADDR);
|
|
56
|
+
putRack("rack.url_scheme", RACK_KEY.URL_SCHEME);
|
|
57
|
+
putRack("rack.version", RACK_KEY.VERSION);
|
|
58
|
+
putRack("rack.multithread", RACK_KEY.MULTITHREAD);
|
|
59
|
+
putRack("rack.multiprocess", RACK_KEY.MULTIPROCESS);
|
|
60
|
+
putRack("rack.run_once", RACK_KEY.RUN_ONCE);
|
|
61
|
+
putRack("CONTENT_LENGTH", RACK_KEY.CONTENT_LENGTH);
|
|
62
|
+
putRack("HTTPS", RACK_KEY.HTTPS);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private void putRack(String key, RACK_KEY value) {
|
|
66
|
+
rackKeyMap.put(RubyHelper.toUsAsciiRubyString(runtime, key), value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public RubyHash getEnv(final HttpServerRequest request,
|
|
70
|
+
final RackInput input,
|
|
71
|
+
final boolean isSSL) throws IOException {
|
|
72
|
+
MultiMap headers = request.headers();
|
|
73
|
+
final RackEnvironmentHash env = new RackEnvironmentHash(runtime, headers, rackKeyMap);
|
|
74
|
+
env.lazyPut(RACK_KEY.RACK_INPUT, input, false);
|
|
75
|
+
env.lazyPut(RACK_KEY.RACK_ERRORS, errors, false);
|
|
76
|
+
|
|
77
|
+
String pathInfo = request.path();
|
|
78
|
+
|
|
79
|
+
String scriptName = "";
|
|
80
|
+
String[] hostInfo = getHostInfo(request.headers().get(Const.HOST));
|
|
81
|
+
|
|
82
|
+
env.lazyPut(RACK_KEY.REQUEST_METHOD, request.method(), true);
|
|
83
|
+
env.lazyPut(RACK_KEY.SCRIPT_NAME, scriptName, false);
|
|
84
|
+
env.lazyPut(RACK_KEY.PATH_INFO, pathInfo, false);
|
|
85
|
+
env.lazyPut(RACK_KEY.QUERY_STRING, orEmpty(request.query()), false);
|
|
86
|
+
env.lazyPut(RACK_KEY.SERVER_NAME, hostInfo[0], false);
|
|
87
|
+
env.lazyPut(RACK_KEY.SERVER_PORT, hostInfo[1], true);
|
|
88
|
+
env.lazyPut(RACK_KEY.HTTP_VERSION,
|
|
89
|
+
request.version() == HttpVersion.HTTP_1_1 ? Const.HTTP_11 : Const.HTTP_10, true);
|
|
90
|
+
env.lazyPut(RACK_KEY.CONTENT_TYPE, headers.get(HttpHeaders.Names.CONTENT_TYPE), true);
|
|
91
|
+
env.lazyPut(RACK_KEY.REQUEST_URI, request.uri(), false);
|
|
92
|
+
env.lazyPut(RACK_KEY.REMOTE_ADDR, getRemoteAddr(request), true);
|
|
93
|
+
env.lazyPut(RACK_KEY.URL_SCHEME, isSSL? Const.HTTPS : Const.HTTP, true);
|
|
94
|
+
env.lazyPut(RACK_KEY.VERSION, rackVersion, false);
|
|
95
|
+
env.lazyPut(RACK_KEY.MULTITHREAD, runtime.getTrue(), false);
|
|
96
|
+
env.lazyPut(RACK_KEY.MULTIPROCESS, runtime.getFalse(), false);
|
|
97
|
+
env.lazyPut(RACK_KEY.RUN_ONCE, runtime.getFalse(), false);
|
|
98
|
+
|
|
99
|
+
final int contentLength = getContentLength(headers);
|
|
100
|
+
if (contentLength >= 0) {
|
|
101
|
+
env.lazyPut(RACK_KEY.CONTENT_LENGTH, contentLength + "", true);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (isSSL) {
|
|
105
|
+
env.lazyPut(RACK_KEY.HTTPS, "on", true);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return env;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public String[] getHostInfo(String host) {
|
|
112
|
+
String[] hostInfo;
|
|
113
|
+
if (host != null) {
|
|
114
|
+
int colon = host.indexOf(":");
|
|
115
|
+
if (colon > 0)
|
|
116
|
+
hostInfo = new String[]{host.substring(0, colon), host.substring(colon + 1)};
|
|
117
|
+
else
|
|
118
|
+
hostInfo = new String[]{host, Const.PORT_80};
|
|
119
|
+
|
|
120
|
+
} else {
|
|
121
|
+
hostInfo = new String[]{Const.LOCALHOST, Const.PORT_80};
|
|
122
|
+
}
|
|
123
|
+
return hostInfo;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private static String getRemoteAddr(final HttpServerRequest request) {
|
|
127
|
+
InetSocketAddress sourceAddress = request.remoteAddress();
|
|
128
|
+
if(sourceAddress == null) {
|
|
129
|
+
return "";
|
|
130
|
+
}
|
|
131
|
+
return sourceAddress.getHostString();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private static int getContentLength(final MultiMap headers) {
|
|
135
|
+
final String contentLengthStr = headers.get(HttpHeaders.Names.CONTENT_LENGTH);
|
|
136
|
+
if (contentLengthStr == null || contentLengthStr.isEmpty()) {
|
|
137
|
+
return -1;
|
|
138
|
+
}
|
|
139
|
+
return Integer.parseInt(contentLengthStr);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private String orEmpty(String val) {
|
|
143
|
+
return val == null ? "" : val;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private final Ruby runtime;
|
|
147
|
+
private final RubyArray rackVersion;
|
|
148
|
+
private final RubyIO errors;
|
|
149
|
+
private final Map<RubyString, RACK_KEY> rackKeyMap = new HashMap<>();
|
|
150
|
+
}
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
package org.jruby.jubilee;
|
|
2
|
+
|
|
3
|
+
import io.netty.handler.codec.http.HttpHeaders;
|
|
4
|
+
import org.jruby.Ruby;
|
|
5
|
+
import org.jruby.RubyArray;
|
|
6
|
+
import org.jruby.RubyBoolean;
|
|
7
|
+
import org.jruby.RubyFixnum;
|
|
8
|
+
import org.jruby.RubyHash;
|
|
9
|
+
import org.jruby.RubyString;
|
|
10
|
+
import org.jruby.runtime.Block;
|
|
11
|
+
import org.jruby.runtime.ThreadContext;
|
|
12
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
|
13
|
+
import org.jruby.jubilee.utils.RubyHelper;
|
|
14
|
+
import org.vertx.java.core.MultiMap;
|
|
15
|
+
|
|
16
|
+
import java.util.HashMap;
|
|
17
|
+
import java.util.List;
|
|
18
|
+
import java.util.Map;
|
|
19
|
+
|
|
20
|
+
public class RackEnvironmentHash extends RubyHash {
|
|
21
|
+
|
|
22
|
+
public RackEnvironmentHash(final Ruby runtime, final MultiMap headers,
|
|
23
|
+
final Map<RubyString, RackEnvironment.RACK_KEY> rackKeyMap) {
|
|
24
|
+
super(runtime);
|
|
25
|
+
this.headers = headers;
|
|
26
|
+
this.rackKeyMap = rackKeyMap;
|
|
27
|
+
this.headerKeyMap = new HashMap<>();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public void lazyPut(RackEnvironment.RACK_KEY rackKey, final Object value, boolean usAscii) {
|
|
31
|
+
rackValues[rackKey.ordinal()] = value;
|
|
32
|
+
usAsciiValues[rackKey.ordinal()] = usAscii;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// synchronized probably isn't needed here since we create a new RackEnvironment
|
|
36
|
+
// per request, but we can't guarantee users aren't spawning a new thread and
|
|
37
|
+
// passing the env to that new thread
|
|
38
|
+
private synchronized void fillKey(final IRubyObject rubyKey) {
|
|
39
|
+
if (!filledEntireHash) {
|
|
40
|
+
if (rubyKey instanceof RubyString && !containsKey(rubyKey)) {
|
|
41
|
+
if (! filledHeaderKeyMap) populateHeaderKeyMap();
|
|
42
|
+
byte[] keyBytes = ((RubyString) rubyKey).getBytes();
|
|
43
|
+
if (keyBytes.length > 5 && keyBytes[0] == 'H'
|
|
44
|
+
&& keyBytes[1] == 'T' && keyBytes[2] == 'T'
|
|
45
|
+
&& keyBytes[3] == 'P' && keyBytes[4] == '_') {
|
|
46
|
+
|
|
47
|
+
fillHeaderKey((RubyString) rubyKey);
|
|
48
|
+
} else {
|
|
49
|
+
fillRackKey((RubyString) rubyKey);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
private synchronized void fillEntireHash() {
|
|
55
|
+
if (!filledEntireHash) {
|
|
56
|
+
for (RubyString key : rackKeyMap.keySet()) {
|
|
57
|
+
fillRackKey(key);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (String headerKey : headers.names()) {
|
|
61
|
+
RubyString rubyKey = RubyHelper.toUsAsciiRubyString(getRuntime(), rackHeaderNameToBytes(headerKey));
|
|
62
|
+
putHeaderKey(rubyKey, headerKey);
|
|
63
|
+
fillHeaderKey(rubyKey, headerKey);
|
|
64
|
+
}
|
|
65
|
+
filledEntireHash = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
private synchronized void fillRackKey(final RubyString key) {
|
|
69
|
+
RackEnvironment.RACK_KEY rackKey = rackKeyMap.get(key);
|
|
70
|
+
if (rackKey != null) {
|
|
71
|
+
Object value = rackValues[rackKey.ordinal()];
|
|
72
|
+
if (value != null) {
|
|
73
|
+
if (value instanceof String) {
|
|
74
|
+
boolean usAscii = usAsciiValues[rackKey.ordinal()];
|
|
75
|
+
RubyString rubyValue = usAscii ? RubyHelper.toUsAsciiRubyString(getRuntime(), (String) value) :
|
|
76
|
+
RubyHelper.toUnicodeRubyString(getRuntime(), (String) value);
|
|
77
|
+
put(key, rubyValue);
|
|
78
|
+
} else {
|
|
79
|
+
put(key, value);
|
|
80
|
+
}
|
|
81
|
+
rackValues[rackKey.ordinal()] = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private synchronized void populateHeaderKeyMap() {
|
|
87
|
+
for (String key : headers.names()) {
|
|
88
|
+
byte[] rubyKeyBytes = rackHeaderNameToBytes(key);
|
|
89
|
+
RubyString rubyKey = RubyHelper.toUsAsciiRubyString(getRuntime(), rubyKeyBytes);
|
|
90
|
+
putHeaderKey(rubyKey, key);
|
|
91
|
+
}
|
|
92
|
+
this.filledHeaderKeyMap = true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private void putHeaderKey(RubyString rubyKey, String key) {
|
|
96
|
+
this.headerKeyMap.put(rubyKey, key);
|
|
97
|
+
}
|
|
98
|
+
private synchronized void fillHeaderKey(final RubyString rubyKey) {
|
|
99
|
+
String headerKey = this.headerKeyMap.get(rubyKey);
|
|
100
|
+
if (headerKey != null) fillHeaderKey(rubyKey, headerKey);
|
|
101
|
+
}
|
|
102
|
+
private synchronized void fillHeaderKey(final RubyString rubyKey, String key) {
|
|
103
|
+
// RACK spec says not to create HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH headers
|
|
104
|
+
if (!key.equals(HttpHeaders.Names.CONTENT_TYPE) && !key.equals(HttpHeaders.Names.CONTENT_LENGTH)) {
|
|
105
|
+
List<String> headerValues = headers.getAll(key);
|
|
106
|
+
if (!headerValues.isEmpty()) {
|
|
107
|
+
String headerValue = headerValues.get(0);
|
|
108
|
+
int valueIndex = 1;
|
|
109
|
+
while (valueIndex < headerValues.size()) {
|
|
110
|
+
headerValue += "\n" + headerValues.get(valueIndex++);
|
|
111
|
+
}
|
|
112
|
+
put(rubyKey, RubyHelper.toUnicodeRubyString(getRuntime(), headerValue));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private static byte[] rackHeaderNameToBytes(final String headerName) {
|
|
118
|
+
// This is a more performant implemention of:
|
|
119
|
+
// "HTTP_" + headerName.toUpperCase().replace('-', '_');
|
|
120
|
+
byte[] envNameBytes = new byte[headerName.length() + 5];
|
|
121
|
+
envNameBytes[0] = 'H';
|
|
122
|
+
envNameBytes[1] = 'T';
|
|
123
|
+
envNameBytes[2] = 'T';
|
|
124
|
+
envNameBytes[3] = 'P';
|
|
125
|
+
envNameBytes[4] = '_';
|
|
126
|
+
for (int i = 5; i < envNameBytes.length; i++) {
|
|
127
|
+
envNameBytes[i] = (byte) rackHeaderize(headerName.charAt(i - 5));
|
|
128
|
+
}
|
|
129
|
+
return envNameBytes;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private static char rackHeaderize(char c) {
|
|
133
|
+
if (c == '-') {
|
|
134
|
+
c = '_';
|
|
135
|
+
}
|
|
136
|
+
return toUpperCase(c);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private static char toUpperCase(char c) {
|
|
140
|
+
if (c >= 'a' && c <= 'z') {
|
|
141
|
+
c -= 32;
|
|
142
|
+
}
|
|
143
|
+
return c;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private final Object[] rackValues = new Object[RackEnvironment.NUM_RACK_KEYS];
|
|
147
|
+
private final boolean[] usAsciiValues = new boolean[RackEnvironment.NUM_RACK_KEYS];
|
|
148
|
+
private final MultiMap headers;
|
|
149
|
+
private final Map<RubyString, RackEnvironment.RACK_KEY> rackKeyMap;
|
|
150
|
+
private final Map<RubyString, String> headerKeyMap;
|
|
151
|
+
private boolean filledEntireHash = false;
|
|
152
|
+
private boolean filledHeaderKeyMap = false;
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
//
|
|
157
|
+
// Overridden RubyHash methods that operate on individual keys
|
|
158
|
+
//
|
|
159
|
+
@Override
|
|
160
|
+
public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
|
|
161
|
+
fillKey(key);
|
|
162
|
+
return super.op_aref(context, key);
|
|
163
|
+
}
|
|
164
|
+
@Override
|
|
165
|
+
public IRubyObject fetch(ThreadContext context, IRubyObject key, Block block) {
|
|
166
|
+
fillKey(key);
|
|
167
|
+
return super.fetch(context, key, block);
|
|
168
|
+
}
|
|
169
|
+
@Override
|
|
170
|
+
public IRubyObject fetch(ThreadContext context, IRubyObject key, IRubyObject _default, Block block) {
|
|
171
|
+
fillKey(key);
|
|
172
|
+
return super.fetch(context, key ,_default, block);
|
|
173
|
+
}
|
|
174
|
+
@Override
|
|
175
|
+
public RubyBoolean has_key_p(IRubyObject key) {
|
|
176
|
+
fillKey(key);
|
|
177
|
+
return super.has_key_p(key);
|
|
178
|
+
}
|
|
179
|
+
@Override
|
|
180
|
+
public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) {
|
|
181
|
+
fillKey(key);
|
|
182
|
+
return super.op_aset(context, key, value);
|
|
183
|
+
}
|
|
184
|
+
@Override
|
|
185
|
+
public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
|
|
186
|
+
fillKey(key);
|
|
187
|
+
return super.delete(context, key, block);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
//
|
|
191
|
+
// Overridden RubyHash methods that don't operate on individual keys so we
|
|
192
|
+
// fill the entire hash
|
|
193
|
+
//
|
|
194
|
+
@Override
|
|
195
|
+
public IRubyObject inspect(ThreadContext context) {
|
|
196
|
+
fillEntireHash();
|
|
197
|
+
return super.inspect(context);
|
|
198
|
+
}
|
|
199
|
+
@Override
|
|
200
|
+
public IRubyObject inspect19(ThreadContext context) {
|
|
201
|
+
fillEntireHash();
|
|
202
|
+
return super.inspect19(context);
|
|
203
|
+
}
|
|
204
|
+
@Override
|
|
205
|
+
public RubyFixnum rb_size() {
|
|
206
|
+
fillEntireHash();
|
|
207
|
+
return super.rb_size();
|
|
208
|
+
}
|
|
209
|
+
@Override
|
|
210
|
+
public RubyBoolean empty_p() {
|
|
211
|
+
fillEntireHash();
|
|
212
|
+
return super.empty_p();
|
|
213
|
+
}
|
|
214
|
+
@Override
|
|
215
|
+
public RubyArray to_a() {
|
|
216
|
+
fillEntireHash();
|
|
217
|
+
return super.to_a();
|
|
218
|
+
}
|
|
219
|
+
@Override
|
|
220
|
+
public IRubyObject to_s(ThreadContext context) {
|
|
221
|
+
fillEntireHash();
|
|
222
|
+
return super.to_s(context);
|
|
223
|
+
}
|
|
224
|
+
@Override
|
|
225
|
+
public IRubyObject to_s19(ThreadContext context) {
|
|
226
|
+
fillEntireHash();
|
|
227
|
+
return super.to_s19(context);
|
|
228
|
+
}
|
|
229
|
+
@Override
|
|
230
|
+
public RubyHash rehash() {
|
|
231
|
+
fillEntireHash();
|
|
232
|
+
return super.rehash();
|
|
233
|
+
}
|
|
234
|
+
@Override
|
|
235
|
+
public IRubyObject op_equal(final ThreadContext context, IRubyObject other) {
|
|
236
|
+
fillEntireHash();
|
|
237
|
+
return super.op_equal(context, other);
|
|
238
|
+
}
|
|
239
|
+
@Override
|
|
240
|
+
public IRubyObject op_eql19(final ThreadContext context, IRubyObject other) {
|
|
241
|
+
fillEntireHash();
|
|
242
|
+
return super.op_eql19(context, other);
|
|
243
|
+
}
|
|
244
|
+
@Override
|
|
245
|
+
public RubyFixnum hash() {
|
|
246
|
+
fillEntireHash();
|
|
247
|
+
return super.hash();
|
|
248
|
+
}
|
|
249
|
+
@Override
|
|
250
|
+
public RubyFixnum hash19() {
|
|
251
|
+
fillEntireHash();
|
|
252
|
+
return super.hash19();
|
|
253
|
+
}
|
|
254
|
+
@Override
|
|
255
|
+
public IRubyObject fetch(ThreadContext context, IRubyObject[] args, Block block) {
|
|
256
|
+
fillEntireHash();
|
|
257
|
+
return super.fetch(context, args, block);
|
|
258
|
+
}
|
|
259
|
+
@Override
|
|
260
|
+
public RubyBoolean has_value_p(ThreadContext context, IRubyObject expected) {
|
|
261
|
+
fillEntireHash();
|
|
262
|
+
return super.has_value_p(context, expected);
|
|
263
|
+
}
|
|
264
|
+
@Override
|
|
265
|
+
public IRubyObject each(final ThreadContext context, final Block block) {
|
|
266
|
+
fillEntireHash();
|
|
267
|
+
return super.each(context, block);
|
|
268
|
+
}
|
|
269
|
+
@Override
|
|
270
|
+
public IRubyObject each19(final ThreadContext context, final Block block) {
|
|
271
|
+
fillEntireHash();
|
|
272
|
+
return super.each19(context, block);
|
|
273
|
+
}
|
|
274
|
+
@Override
|
|
275
|
+
public IRubyObject each_value(final ThreadContext context, final Block block) {
|
|
276
|
+
fillEntireHash();
|
|
277
|
+
return super.each_value(context, block);
|
|
278
|
+
}
|
|
279
|
+
@Override
|
|
280
|
+
public IRubyObject each_key(final ThreadContext context, final Block block) {
|
|
281
|
+
fillEntireHash();
|
|
282
|
+
return super.each_key(context, block);
|
|
283
|
+
}
|
|
284
|
+
@Override
|
|
285
|
+
public IRubyObject select_bang(final ThreadContext context, final Block block) {
|
|
286
|
+
fillEntireHash();
|
|
287
|
+
return super.select_bang(context, block);
|
|
288
|
+
}
|
|
289
|
+
@Override
|
|
290
|
+
public IRubyObject keep_if(final ThreadContext context, final Block block) {
|
|
291
|
+
fillEntireHash();
|
|
292
|
+
return super.keep_if(context, block);
|
|
293
|
+
}
|
|
294
|
+
@Override
|
|
295
|
+
public IRubyObject sort(ThreadContext context, Block block) {
|
|
296
|
+
fillEntireHash();
|
|
297
|
+
return super.sort(context, block);
|
|
298
|
+
}
|
|
299
|
+
@Override
|
|
300
|
+
public IRubyObject index(ThreadContext context, IRubyObject expected) {
|
|
301
|
+
fillEntireHash();
|
|
302
|
+
return super.index(context, expected);
|
|
303
|
+
}
|
|
304
|
+
@Override
|
|
305
|
+
public IRubyObject index19(ThreadContext context, IRubyObject expected) {
|
|
306
|
+
fillEntireHash();
|
|
307
|
+
return super.index19(context, expected);
|
|
308
|
+
}
|
|
309
|
+
@Override
|
|
310
|
+
public IRubyObject key(ThreadContext context, IRubyObject expected) {
|
|
311
|
+
fillEntireHash();
|
|
312
|
+
return super.key(context, expected);
|
|
313
|
+
}
|
|
314
|
+
@Override
|
|
315
|
+
public RubyArray indices(ThreadContext context, IRubyObject[] indices) {
|
|
316
|
+
fillEntireHash();
|
|
317
|
+
return super.indices(context, indices);
|
|
318
|
+
}
|
|
319
|
+
@Override
|
|
320
|
+
public RubyArray keys() {
|
|
321
|
+
fillEntireHash();
|
|
322
|
+
return super.keys();
|
|
323
|
+
}
|
|
324
|
+
@Override
|
|
325
|
+
public RubyArray rb_values() {
|
|
326
|
+
fillEntireHash();
|
|
327
|
+
return super.rb_values();
|
|
328
|
+
}
|
|
329
|
+
@Override
|
|
330
|
+
public IRubyObject shift(ThreadContext context) {
|
|
331
|
+
fillEntireHash();
|
|
332
|
+
return super.shift(context);
|
|
333
|
+
}
|
|
334
|
+
@Override
|
|
335
|
+
public IRubyObject select(final ThreadContext context, final Block block) {
|
|
336
|
+
fillEntireHash();
|
|
337
|
+
return super.select(context, block);
|
|
338
|
+
}
|
|
339
|
+
@Override
|
|
340
|
+
public IRubyObject select19(final ThreadContext context, final Block block) {
|
|
341
|
+
fillEntireHash();
|
|
342
|
+
return super.select19(context, block);
|
|
343
|
+
}
|
|
344
|
+
@Override
|
|
345
|
+
public IRubyObject delete_if(final ThreadContext context, final Block block) {
|
|
346
|
+
fillEntireHash();
|
|
347
|
+
return super.delete_if(context, block);
|
|
348
|
+
}
|
|
349
|
+
@Override
|
|
350
|
+
public IRubyObject reject(final ThreadContext context, final Block block) {
|
|
351
|
+
fillEntireHash();
|
|
352
|
+
return super.reject(context, block);
|
|
353
|
+
}
|
|
354
|
+
@Override
|
|
355
|
+
public IRubyObject reject_bang(final ThreadContext context, final Block block) {
|
|
356
|
+
fillEntireHash();
|
|
357
|
+
return super.reject_bang(context, block);
|
|
358
|
+
}
|
|
359
|
+
@Override
|
|
360
|
+
public RubyHash rb_clear() {
|
|
361
|
+
fillEntireHash();
|
|
362
|
+
return super.rb_clear();
|
|
363
|
+
}
|
|
364
|
+
@Override
|
|
365
|
+
public RubyHash invert(final ThreadContext context) {
|
|
366
|
+
fillEntireHash();
|
|
367
|
+
return super.invert(context);
|
|
368
|
+
}
|
|
369
|
+
@Override
|
|
370
|
+
public RubyHash merge_bang(final ThreadContext context, final IRubyObject other, final Block block) {
|
|
371
|
+
fillEntireHash();
|
|
372
|
+
return super.merge_bang(context, other, block);
|
|
373
|
+
}
|
|
374
|
+
@Override
|
|
375
|
+
public RubyHash merge_bang19(final ThreadContext context, final IRubyObject other, final Block block) {
|
|
376
|
+
fillEntireHash();
|
|
377
|
+
return super.merge_bang19(context, other, block);
|
|
378
|
+
}
|
|
379
|
+
@Override
|
|
380
|
+
public RubyHash merge(ThreadContext context, IRubyObject other, Block block) {
|
|
381
|
+
fillEntireHash();
|
|
382
|
+
return super.merge(context, other, block);
|
|
383
|
+
}
|
|
384
|
+
@Override
|
|
385
|
+
public RubyHash initialize_copy(ThreadContext context, IRubyObject other) {
|
|
386
|
+
fillEntireHash();
|
|
387
|
+
return super.initialize_copy(context, other);
|
|
388
|
+
}
|
|
389
|
+
@Override
|
|
390
|
+
public RubyHash initialize_copy19(ThreadContext context, IRubyObject other) {
|
|
391
|
+
fillEntireHash();
|
|
392
|
+
return super.initialize_copy19(context, other);
|
|
393
|
+
}
|
|
394
|
+
@Override
|
|
395
|
+
public RubyHash replace(final ThreadContext context, IRubyObject other) {
|
|
396
|
+
fillEntireHash();
|
|
397
|
+
return super.replace(context, other);
|
|
398
|
+
}
|
|
399
|
+
@Override
|
|
400
|
+
public RubyHash replace19(final ThreadContext context, IRubyObject other) {
|
|
401
|
+
fillEntireHash();
|
|
402
|
+
return super.replace19(context, other);
|
|
403
|
+
}
|
|
404
|
+
@Override
|
|
405
|
+
public RubyArray values_at(ThreadContext context, IRubyObject[] args) {
|
|
406
|
+
fillEntireHash();
|
|
407
|
+
return super.values_at(context, args);
|
|
408
|
+
}
|
|
409
|
+
@Override
|
|
410
|
+
public IRubyObject assoc(final ThreadContext context, final IRubyObject obj) {
|
|
411
|
+
fillEntireHash();
|
|
412
|
+
return super.assoc(context, obj);
|
|
413
|
+
}
|
|
414
|
+
@Override
|
|
415
|
+
public IRubyObject rassoc(final ThreadContext context, final IRubyObject obj) {
|
|
416
|
+
fillEntireHash();
|
|
417
|
+
return super.rassoc(context, obj);
|
|
418
|
+
}
|
|
419
|
+
@Override
|
|
420
|
+
public IRubyObject flatten(ThreadContext context) {
|
|
421
|
+
fillEntireHash();
|
|
422
|
+
return super.flatten(context);
|
|
423
|
+
}
|
|
424
|
+
@Override
|
|
425
|
+
public IRubyObject flatten(ThreadContext context, IRubyObject level) {
|
|
426
|
+
fillEntireHash();
|
|
427
|
+
return super.flatten(context, level);
|
|
428
|
+
}
|
|
429
|
+
@Override
|
|
430
|
+
public IRubyObject getCompareByIdentity(ThreadContext context) {
|
|
431
|
+
fillEntireHash();
|
|
432
|
+
return super.getCompareByIdentity(context);
|
|
433
|
+
}
|
|
434
|
+
@Override
|
|
435
|
+
public IRubyObject getCompareByIdentity_p(ThreadContext context) {
|
|
436
|
+
fillEntireHash();
|
|
437
|
+
return super.getCompareByIdentity_p(context);
|
|
438
|
+
}
|
|
439
|
+
@Override
|
|
440
|
+
public IRubyObject dup(ThreadContext context) {
|
|
441
|
+
fillEntireHash();
|
|
442
|
+
return super.dup(context);
|
|
443
|
+
}
|
|
444
|
+
@Override
|
|
445
|
+
public IRubyObject rbClone(ThreadContext context) {
|
|
446
|
+
fillEntireHash();
|
|
447
|
+
return super.rbClone(context);
|
|
448
|
+
}
|
|
449
|
+
}
|