Skip to content

Commit

Permalink
Ruby: Handle response field arrays.
Browse files Browse the repository at this point in the history
@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>
  • Loading branch information
ac000 committed Nov 8, 2023
1 parent ebf31d2 commit 8ea2dce
Showing 1 changed file with 79 additions and 1 deletion.
80 changes: 79 additions & 1 deletion src/ruby/nxt_ruby.c
Original file line number Diff line number Diff line change
Expand Up @@ -901,13 +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 && TYPE(r_value) != T_NIL)) {
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;
}

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;

Expand Down Expand Up @@ -953,6 +984,53 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg)

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;

Expand Down

0 comments on commit 8ea2dce

Please sign in to comment.