Skip to content

Commit d9f5f1f

Browse files
committed
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 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>
1 parent 846a7f4 commit d9f5f1f

File tree

1 file changed

+68
-3
lines changed

1 file changed

+68
-3
lines changed

src/ruby/nxt_ruby.c

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -889,13 +889,37 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg)
889889
goto fail;
890890
}
891891

892-
if (nxt_slow_path(TYPE(r_value) != T_STRING)) {
892+
if (nxt_slow_path(TYPE(r_value) != T_STRING && TYPE(r_value) != T_ARRAY)) {
893893
nxt_unit_req_error(headers_info->req,
894894
"Ruby: Wrong header entry 'value' from application");
895895

896896
goto fail;
897897
}
898898

899+
if (TYPE(r_value) == T_ARRAY) {
900+
int i;
901+
int arr_len = RARRAY_LEN(r_value);
902+
VALUE item;
903+
size_t len = 0;
904+
905+
for (i = 0; i < arr_len; i++) {
906+
item = rb_ary_entry(r_value, i);
907+
if (TYPE(item) != T_STRING) {
908+
nxt_unit_req_error(headers_info->req,
909+
"Ruby: Wrong header entry in 'value' array "
910+
"from application");
911+
goto fail;
912+
}
913+
914+
len += RSTRING_LEN(item) + 2; /* +2 for '; ' */
915+
}
916+
917+
headers_info->fields++;
918+
headers_info->size += RSTRING_LEN(r_key) + len - 2;
919+
920+
return ST_CONTINUE;
921+
}
922+
899923
value = RSTRING_PTR(r_value);
900924
value_end = value + RSTRING_LEN(r_value);
901925

@@ -941,11 +965,52 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg)
941965
headers_info = (void *) (uintptr_t) arg;
942966
rc = &headers_info->rc;
943967

968+
key_len = RSTRING_LEN(r_key);
969+
970+
if (TYPE(r_value) == T_ARRAY) {
971+
int i;
972+
int arr_len = RARRAY_LEN(r_value);
973+
char *field, *p;
974+
VALUE item;
975+
size_t len = 0;
976+
977+
for (i = 0; i < arr_len; i++) {
978+
item = rb_ary_entry(r_value, i);
979+
980+
len += RSTRING_LEN(item) + 2; /* +2 for '; ' */
981+
}
982+
983+
field = nxt_unit_malloc(NULL, len);
984+
if (field == NULL) {
985+
goto fail;
986+
}
987+
988+
p = field;
989+
990+
for (i = 0; i < arr_len; i++) {
991+
item = rb_ary_entry(r_value, i);
992+
993+
p = nxt_cpymem(p, RSTRING_PTR(item), RSTRING_LEN(item));
994+
p = nxt_cpymem(p, "; ", 2);
995+
}
996+
997+
len -= 2;
998+
999+
*rc = nxt_unit_response_add_field(headers_info->req,
1000+
RSTRING_PTR(r_key), key_len,
1001+
field, len);
1002+
nxt_unit_free(NULL, field);
1003+
1004+
if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
1005+
goto fail;
1006+
}
1007+
1008+
return ST_CONTINUE;
1009+
}
1010+
9441011
value = RSTRING_PTR(r_value);
9451012
value_end = value + RSTRING_LEN(r_value);
9461013

947-
key_len = RSTRING_LEN(r_key);
948-
9491014
pos = value;
9501015

9511016
for ( ;; ) {

0 commit comments

Comments
 (0)