Martin Posthumus <[email protected]> wrote:
> > OK.  unicorn has no choice to support all Rack as long as Rack <= 2
> > applications exist...
> 
> Understood, I just wanted to show that I'm reasonably confident this isn't
> due to something weird I was doing in application code, and that ultimately
> I couldn't find any workaround that didn't involve monkeypatching either
> Rack or Unicorn.
> 
> > Does this work for you?
> 
> Indeed it does!

Thanks for the confirmation and good to know.

> > Yeah, I haven't looked deeply at Rack 3 support and hate dealing
> > with the culture of breaking changes prevalent in the Ruby world :<
> 
> Yeah, I get it. To be fair the array representation probably makes more
> sense for a header with multiple values, but downstream in practice it means
> you have to deal with both.

Yes, and Rack 3 also breaks the array of arrays `[ [ k, v1 ], [ k, v2 ] ]'
representation I was relying on :<

In any case, I'm thinking about hoisting out append_header()
into its own method to reuse between normal responses and
rack.early_hints 103 responses.

Cc-ing Jean to see if anything is amiss with the following
since they wrote the original code.

Original Rack 3 headers patch at:
   https://yhbt.net/unicorn-public/20221210024246.GA8584@dcvr/


diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb
index 3308c9be..19469b47 100644
--- a/lib/unicorn/http_response.rb
+++ b/lib/unicorn/http_response.rb
@@ -19,6 +19,18 @@ def err_response(code, response_start_sent)
       "#{code} #{STATUS_CODES[code]}\r\n\r\n"
   end
 
+  def append_header(buf, key, value)
+    case value
+    when Array # Rack 3
+      value.each { |v| buf << "#{key}: #{v}\r\n" }
+    when /\n/ # Rack 2
+      # avoiding blank, key-only cookies with /\n+/
+      value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
+    else
+      buf << "#{key}: #{value}\r\n"
+    end
+  end
+
   # writes the rack_response to socket as an HTTP response
   def http_response_write(socket, status, headers, body,
                           req = Unicorn::HttpRequest.new)
@@ -40,15 +52,7 @@ def http_response_write(socket, status, headers, body,
           # key in Rack < 1.5
           hijack = value
         else
-          case value
-          when Array # Rack 3
-            value.each { |v| buf << "#{key}: #{v}\r\n" }
-          when /\n/ # Rack 2
-            # avoiding blank, key-only cookies with /\n+/
-            value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
-          else
-            buf << "#{key}: #{value}\r\n"
-          end
+          append_header(buf, key, value)
         end
       end
       socket.write(buf << "\r\n".freeze)
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 3416808f..cad515b9 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -589,22 +589,11 @@ def handle_error(client, e)
   end
 
   def e103_response_write(client, headers)
-    response = if @request.response_start_sent
-      "103 Early Hints\r\n"
-    else
-      "HTTP/1.1 103 Early Hints\r\n"
-    end
-
-    headers.each_pair do |k, vs|
-      next if !vs || vs.empty?
-      values = vs.to_s.split("\n".freeze)
-      values.each do |v|
-        response << "#{k}: #{v}\r\n"
-      end
-    end
-    response << "\r\n".freeze
-    response << "HTTP/1.1 ".freeze if @request.response_start_sent
-    client.write(response)
+    rss = @request.response_start_sent
+    buf = rss ? "103 Early Hints\r\n" : "HTTP/1.1 103 Early Hints\r\n"
+    headers.each { |key, value| append_header(buf, key, value) }
+    buf << (rss ? "\r\nHTTP/1.1 ".freeze : "\r\n".freeze)
+    client.write(buf)
   end
 
   def e100_response_write(client, env)

Reply via email to