New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Doesn't work after rails 7.1 update #974
Comments
Hi @xeron thanks for reaching out! Are you able to share a simple code example on how to reproduce it? This would be great! Furthermore - can you help us investigate and fix this issue? It sounds like there was a change from Line 892 in 5265b7e
Going up the call stack real quick we are getting the response headers from rack and checking if these are from type Line 842 in 5265b7e
Iterating over all headers and calling On our website we are not mentioning anything about the version of Rack we are supporting. https://unit.nginx.org/keyfeatures/#supported-app-languages |
@ac000 I know it is good to look into something different but to me this looks like that we are telling the ruby application we are want the rack version
#define NXT_RUBY_RACK_API_VERSION_MAJOR 1
#define NXT_RUBY_RACK_API_VERSION_MINOR 3
version = rb_ary_new();
rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR));
rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR));
rb_hash_aset(hash_env, rb_str_new2("SCRIPT_NAME"), rb_str_new("", 0));
rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); |
I seem to have managed to setup a Rails 7.1.1 environment on Fedora 38 and created the basic rails app from here by doing $ rails new gh-974 My Unit config is simply {
"listeners": {
"[::1]:8080": {
"pass": "applications/app"
}
},
"applications": {
"app": {
"type": "ruby",
"working_directory": "/home/andrew/src/ruby/gh-974",
"script": "config.ru"
}
}
} Hitting the app gives... <!DOCTYPE html>
<html>
<head>
<title>Ruby on Rails 7.1.1</title>
...
<ul>
<li><strong>Rails version:</strong> 7.1.1</li>
<li><strong>Ruby version:</strong> ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]</li>
</ul>
</body>
</html> This is using Rack 3.0.8 as confirmed by strace(1) 15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/head.rb", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/head.rb", O_RDONLY) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/conditional_get.rb", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/conditional_get.rb", O_RDONLY) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/etag.rb", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/etag.rb", O_RDONLY) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/tempfile_reaper.rb", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/tempfile_reaper.rb", O_RDONLY) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/files.rb", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = 18
15685 openat(AT_FDCWD, "/home/andrew/.local/share/gem/ruby/gems/rack-3.0.8/lib/rack/files.rb", O_RDONLY) = 18 |
Not sure why that's hard-coded like that, I guess that was the current version at the time that ruby support was added in 37051b6. Although in practice it doesn't seem to cause an issue, it should probably be auto-detected somehow... |
This looks good - Have the same behaviour with a very simple run do |env|
[200, {}, ["Hello World"]]
end from https://github.com/rack/rack I am using our latest Docker Container |
Thanks @ac000 for looking into this. We are pasing this along into the context, but I am not sure what the application is doing with it? Will try to find out. |
Hey thanks for looking into that. I can't share the app in question but I'll try to create a simple reproducible app later. One specific thing about the app is it sets
|
@tippexs here's a minimal app which triggers the issue – https://github.com/xeron/gh-974-xeron Run |
@xeron Thanks for the updates.
Looks like that's on by default.
Produced no error. Using your provided application under Fedora 38 also produces no error. Trying to build the docker image under Debian 11 results in the following error Step 6/9 : RUN bundle install
---> Running in 6a26e3533fda
Bundler 2.4.10 is running, but your lockfile was generated with 2.4.21. Installing Bundler 2.4.21 and restarting using that version.
Fetching gem metadata from https://rubygems.org/.
Fetching bundler 2.4.21
Installing bundler 2.4.21
Fetching gem metadata from https://rubygems.org/.........
Fetching bigdecimal 3.1.4
Fetching concurrent-ruby 1.2.2
Installing bigdecimal 3.1.4 with native extensions
Installing concurrent-ruby 1.2.2
Fetching connection_pool 2.4.1
Installing connection_pool 2.4.1
Fetching minitest 5.20.0
Installing minitest 5.20.0
Fetching builder 3.2.4
Installing builder 3.2.4
Fetching erubi 1.12.0
Installing erubi 1.12.0
Fetching racc 1.7.1
Installing racc 1.7.1 with native extensions
Killed
The command '/bin/sh -c bundle install' returned a non-zero code
docker: Error response from daemon: invalid mount config for typd mount path: './unit/config/' mount path must be absolute.
See 'docker run --help'. |
You can replace I'm using Docker for Mac so passing absolute paths into the Docker VM is a complicated story. |
I pushed the commit which removes the need for volume mount and just copies I also published the image so you don't even need to build it:
And in a separate shell:
|
I was able to do some more testing with it this morning. It looks like the issue is with My configuration: {
"listeners": {
"*:8080": {
"pass": "applications/gh-974-xeron"
},
"*:8443": {
"pass": "applications/gh-974-xeron",
"tls": {
"certificate": "bundle1"
}
}
},
"applications": {
"gh-974-xeron": {
"type": "ruby",
"processes": {
"max": 5,
"spare": 1,
"idle_timeout": 60
},
"working_directory": "/app",
"script": "/app/config.ru",
"environment": {
"RAILS_ENV": "production"
}
}
}
} To generate self-signed certs for testing run openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout cert.key -out cert.crt
cat cert.key cert.crt | curl --unix-socket /var/run/control.unit.sock -X PUT --data-binary @- l/certificates/bundle1 My tests WITH
|
Update: I was able to trace down the issue (at least a little bit more) according to the documentation With Crash: # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
config.ssl_options = {redirect: {host: "localhost", port: 8443}} Works: config.force_ssl = true
config.ssl_options = {redirect: false} As soon as the redirect is off it just works! |
Unfortunately this now errors with Step 6/10 : RUN bundle install
---> Running in 02adb08fbad3
Bundler 2.4.10 is running, but your lockfile was generated with 2.4.21. Installing Bundler 2.4.21 and restarting using that version.
Fetching gem metadata from https://rubygems.org/.
Fetching bundler 2.4.21
Installing bundler 2.4.21
Fetching gem metadata from https://rubygems.org/.........
Fetching concurrent-ruby 1.2.2
Fetching bigdecimal 3.1.4
Installing bigdecimal 3.1.4 with native extensions
Installing concurrent-ruby 1.2.2
Fetching connection_pool 2.4.1
Installing connection_pool 2.4.1
Fetching minitest 5.20.0
Installing minitest 5.20.0
Fetching builder 3.2.4
Installing builder 3.2.4
Fetching erubi 1.12.0
Installing erubi 1.12.0
Fetching racc 1.7.1
Installing racc 1.7.1 with native extensions
Fetching crass 1.0.6
Installing crass 1.0.6
Fetching nio4r 2.5.9
Installing nio4r 2.5.9 with native extensions
Killed
The command '/bin/sh -c bundle install' returned a non-zero code: 137
Unable to find image 'gh-974-xeron:latest' locally
docker: Error response from daemon: pull access denied for gh-974-xeron, repository does not exist or may require 'docker login': denied: requested access to the resource is denied.
See 'docker run --help'.
Thanks, but no-go $ docker run -it -p 8080:8080 ixeron/gh-974-xeron
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
standard_init_linux.go:219: exec user process caused: exec format error x86_64 here... But don't worry, I think I know how to reproduce it natively... |
I cannot seem to reproduce this issue... With config.force_ssl set to true or false and if I use http for https, I always get the <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="turbo-visit-control" content="reload">
<title>Action Controller: Exception caught</title> page and never the ruby errors... Although I do always seem to be getting a '500 Internal Server Error' Do I need to do anything special after editing I don't see that file getting read by Unit at all... Hmm, bugger, I'm running in development mode! Now that I switched to production mode, I'm getting a different page <!DOCTYPE html>
<html>
<head>
<title>We're sorry, but something went wrong (500)</title> and application errors in the unit log, but still not those ruby errors. With With it set to With it set to true and trying to hit https direcrly I'm seeing the 500 Internal Server Error. This is the error I'm getting...
In trying a multitude of combinations I have yet to see the ruby error... Switching back to my simple test app... I don't have any routes setup so get 404's, but it seems to be working . |
If someone wants to try this debug patch, it might give some clue... diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index bcb48f6b..554d4d08 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -888,13 +888,78 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg)
goto fail;
}
+ printf("RDBG: r_key : %s\n", StringValuePtr(r_key));
+
+ printf("RDBG: TYPE(r_value) : ");
+ switch (TYPE(r_value)) {
+ case T_NIL:
+ printf("NIL\n");
+ break;
+ case T_OBJECT:
+ printf("OBJECT\n");
+ break;
+ case T_CLASS:
+ printf("CLASS\n");
+ break;
+ case T_MODULE:
+ printf("MODULE\n");
+ break;
+ case T_FLOAT:
+ printf("FLOAT\n");
+ break;
+ case T_STRING:
+ printf("STRING\n");
+ break;
+ case T_REGEXP:
+ printf("REGEXP\n");
+ break;
+ case T_ARRAY:
+ printf("ARRAY\n");
+ break;
+ case T_HASH:
+ printf("HASH\n");
+ break;
+ case T_STRUCT:
+ printf("STRUCT\n");
+ break;
+ case T_BIGNUM:
+ printf("BIGNUM\n");
+ break;
+ case T_FIXNUM:
+ printf("FIXNUM\n");
+ break;
+ case T_COMPLEX:
+ printf("COMPLEX\n");
+ break;
+ case T_RATIONAL:
+ printf("RATIONAL\n");
+ break;
+ case T_FILE:
+ printf("FILE\n");
+ break;
+ case T_TRUE:
+ case T_FALSE:
+ printf("TRUE/FALSE\n");
+ break;
+ case T_DATA:
+ printf("DATA\n");
+ break;
+ case T_SYMBOL:
+ printf("SYMBOL\n");
+ break;
+ default:
+ printf("UNKNOWN\n");
+ }
if (nxt_slow_path(TYPE(r_value) != T_STRING)) {
nxt_unit_req_error(headers_info->req,
"Ruby: Wrong header entry 'value' from application");
+ printf("RDBG: r_value : -\n");
goto fail;
}
+ printf("RDBG: r_value : %s\n", StringValuePtr(r_value));
+
value = RSTRING_PTR(r_value);
value_end = value + RSTRING_LEN(r_value); Just copy and paste the above into a file. e.g
If you get no output, it's all good. Then rebuild and install the ruby language module
is all that's needed. Of course it kind of assumes you've already built and installed Unit from source... Restart Unit, make a request and you should get something like (but I guess you'll get something different...)
printed to stdout. |
I can reproduce the error message with the following unit config {
"listeners": {
"[::1]:8080": {
"pass": "applications/app"
},
},
"applications": {
"app": {
"type": "ruby",
"working_directory": "/home/andrew/src/ruby",
"script": "974.ru"
}
}
} 974.ru app = Proc.new do |env|
["200", {
"Content-Type" => "text/plain",
"X-Empty-Header" => nil,
}, ["Hello World"]]
end
run app $ curl http://localhost:8080/
<!DOCTYPE html><title>Error 503</title><p>Error 503.
You could be getting anything that isn't a |
Just FYI redirect is not the problem in my case because in my test I'm doing the request using In my actual production the rails is behind the nginx which does SSL offload and adds
@ac000 sorry I don't have time to make my example work under every OS / Docker setup. It was created on Intel MacBook using Docker for Mac and I know it works there, other OSes or Docker setups are out of my scope. |
No problem. The reproducer is in fact really simple... If yourself or @tippexs could try the debug patch to confirm you are seeing an empty header that would be helpful. |
Or if you're feeling lucky! You could just try this patch diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index bcb48f6b..2aaac797 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -889,15 +889,20 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg)
goto fail;
}
- if (nxt_slow_path(TYPE(r_value) != T_STRING)) {
+ if (nxt_slow_path(TYPE(r_value) != T_STRING && TYPE(r_value) != T_NIL)) {
nxt_unit_req_error(headers_info->req,
"Ruby: Wrong header entry 'value' from application");
goto fail;
}
- value = RSTRING_PTR(r_value);
- value_end = value + RSTRING_LEN(r_value);
+ if (TYPE(r_value) == T_STRING) {
+ value = RSTRING_PTR(r_value);
+ value_end = value + RSTRING_LEN(r_value);
+ } else {
+ value = "";
+ value_end = value;
+ }
pos = value;
@@ -941,8 +946,13 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg)
headers_info = (void *) (uintptr_t) arg;
rc = &headers_info->rc;
- value = RSTRING_PTR(r_value);
- value_end = value + RSTRING_LEN(r_value);
+ if (TYPE(r_value) == T_STRING) {
+ value = RSTRING_PTR(r_value);
+ value_end = value + RSTRING_LEN(r_value);
+ } else {
+ value = "";
+ value_end = value;
+ }
key_len = RSTRING_LEN(r_key);
|
@ac000 BTW I just realized your build of my sample app fails because of the memory usage. Seems like docker doesn't have enough memory for Anyway, I applied your patch from #974 (comment) and rerun the test:
Quick search through Seems like starting with rack 3.0.0 header could be represented by a String or an Array of Strings. |
On Sun, 22 Oct 2023 13:58:39 -0700 Ivan Larionov ***@***.***> wrote:
RDBG: r_key : set-cookie
RDBG: TYPE(r_value) : ARRAY
OK, that's unexpected.
Seems like starting with rack 3.0.0 header could be represented by a
String or an Array > of Strings.
Yes, you're right as indicated [here](https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26).
Thanks for the pointer.
|
Thanks for testing @xeron Was able to test the same thing! Looks like we have found the root cause! Great work team! |
This patch should make things work It handles empty response headers and array response fields. E.g The following app = Proc.new do |env|
["200", {
"Content-Type" => "text/plain",
"X-Empty-Header" => nil,
"X-Array-Header" => ["Item-1", nil, "Item-3", "Item-4"],
}, ["Hello World\n"]]
end
run app produces $ curl -v http://localhost:8080/
* Trying 127.0.0.1:8080...
* connect to 127.0.0.1 port 8080 failed: Connection refused
* Trying [::1]:8080...
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.0.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< X-Empty-Header:
< X-Array-Header: Item-1; ; Item-3; Item-4
< Server: Unit/1.31.1
< Date: Mon, 23 Oct 2023 13:28:46 GMT
< Transfer-Encoding: chunked
<
Hello World
* Connection #0 to host localhost left intact You'll notice it also handles diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index bcb48f6b..55f9966e 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -874,6 +874,18 @@ nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, VALUE result,
}
+#define NXT_RUBY_SET_HDR_VALUE(r_value, value, value_end) \
+ do { \
+ if (TYPE(r_value) == T_STRING) { \
+ value = RSTRING_PTR(r_value); \
+ value_end = value + RSTRING_LEN(r_value); \
+ } else { \
+ value = ""; \
+ value_end = value; \
+ } \
+ } while (0);
+
+
static int
nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg)
{
@@ -889,16 +901,44 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg)
goto fail;
}
- if (nxt_slow_path(TYPE(r_value) != T_STRING)) {
+ if (nxt_slow_path(TYPE(r_value) != T_STRING
+ && TYPE(r_value) != T_ARRAY
+ && TYPE(r_value) != T_NIL)) {
nxt_unit_req_error(headers_info->req,
"Ruby: Wrong header entry 'value' from application");
goto fail;
}
- value = RSTRING_PTR(r_value);
- value_end = value + RSTRING_LEN(r_value);
+ if (TYPE(r_value) == T_ARRAY) {
+ int i;
+ int arr_len = RARRAY_LEN(r_value);
+ VALUE item;
+ size_t len = 0;
+ for (i = 0; i < arr_len; i++) {
+ item = rb_ary_entry(r_value, i);
+ if (TYPE(item) != T_STRING && TYPE(item) != T_NIL) {
+ nxt_unit_req_error(headers_info->req,
+ "Ruby: Wrong header entry in 'value' array "
+ "from application");
+ goto fail;
+ }
+
+ if (TYPE(item) == T_STRING) {
+ len += RSTRING_LEN(item);
+ }
+
+ len += 2; /* +2 for '; ' */
+ }
+
+ headers_info->fields++;
+ headers_info->size += RSTRING_LEN(r_key) + len - 2;
+
+ return ST_CONTINUE;
+ }
+
+ NXT_RUBY_SET_HDR_VALUE(r_value, value, value_end);
pos = value;
for ( ;; ) {
@@ -941,11 +981,57 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg)
headers_info = (void *) (uintptr_t) arg;
rc = &headers_info->rc;
- value = RSTRING_PTR(r_value);
- value_end = value + RSTRING_LEN(r_value);
-
key_len = RSTRING_LEN(r_key);
+ if (TYPE(r_value) == T_ARRAY) {
+ int i;
+ int arr_len = RARRAY_LEN(r_value);
+ char *field, *p;
+ VALUE item;
+ size_t len = 0;
+
+ for (i = 0; i < arr_len; i++) {
+ item = rb_ary_entry(r_value, i);
+
+ if (TYPE(item) == T_STRING) {
+ len += RSTRING_LEN(item);
+ }
+
+ len += 2; /* +2 for '; ' */
+ }
+
+ field = nxt_malloc(len);
+ if (field == NULL) {
+ goto fail;
+ }
+
+ p = field;
+
+ for (i = 0; i < arr_len; i++) {
+ item = rb_ary_entry(r_value, i);
+ if (TYPE(item) == T_STRING) {
+ p = nxt_cpymem(p, RSTRING_PTR(item), RSTRING_LEN(item));
+ }
+
+ p = nxt_cpymem(p, "; ", 2);
+ }
+
+ len -= 2;
+
+ *rc = nxt_unit_response_add_field(headers_info->req,
+ RSTRING_PTR(r_key), key_len,
+ field, len);
+ nxt_free(field);
+
+ if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
+ goto fail;
+ }
+
+ return ST_CONTINUE;
+ }
+
+ NXT_RUBY_SET_HDR_VALUE(r_value, value, value_end);
+
pos = value;
for ( ;; ) { |
Will apply and give it a spin! Should add some pytest for this as well! But LGTM! @xeron great first issue! Thanks!! |
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with header value types of T_STRING & T_NIL we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", nil, "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <nginx#974> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with header value types of T_STRING & T_NIL we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", nil, "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <nginx#974> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with header value types of T_STRING & T_NIL we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", nil, "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <nginx#974> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with header value types of T_STRING & T_NIL we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", nil, "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <nginx#974> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
Looks like this is one should be invalid since spec require explicitly String or Array:
Also I tried similar example with the rackup and got following error from linter:
P.S. but for some reason it doesn't complain about |
Which 'spec' are you reading? According to section 3.2 of RFC 7230 AIUI it is allowed by the following
I.e 0 or more field-content and I don't see anything there that distinguishes between request/response headers. Also in the past I have been required to send empty headers to the UK's Make Tax Digital service.
(I don't think omitting it was originally an option).
Seems overly restrictive...
Anyway this is probably better discussed in the PR. |
Sure, will reply you there. |
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with a header value type of T_STRING we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", "", "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <nginx#974> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with a header value type of T_STRING we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", "", "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <nginx#974> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with a header value type of T_STRING we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", "", "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <nginx#974> Link: <nginx#998> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
The fix for this has been merged. |
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] nginx#8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with a header value type of T_STRING we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", "", "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <nginx#974> Link: <nginx#998> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
@xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] #8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] #8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with a header value type of T_STRING we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", "", "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: <https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md?plain=1#L26> Reported-by: Ivan Larionov <xeron.oskom@gmail.com> Closes: <#974> Link: <#998> Tested-by: Timo Stark <t.stark@nginx.com> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
Updated a rails app from 7.0 to 7.1. Getting the following error in unit logs:
might be related to rack upgrade from
2.x
to3.x
. Lockingrack
to~> 2.0
fixes the issue.Unit version: 1.31.0.
The text was updated successfully, but these errors were encountered: