From b5fbe70091d1d271f5eca1d907a445d69eac9533 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 23 Oct 2023 14:24:01 +0100 Subject: [PATCH] Ruby: Handle response field arrays. @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 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]: Reported-by: Ivan Larionov Closes: Tested-by: Timo Stark Signed-off-by: Andrew Clayton --- src/ruby/nxt_ruby.c | 80 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 1ab6c07cb..35f7ab02d 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -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; @@ -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;