@@ -362,7 +362,7 @@ public IRubyObject write_headers(final ThreadContext context, final IRubyObject
362362 return context .nil ;
363363 }
364364
365- private static final ByteList NEW_LINE = new ByteList ( new byte [] { '\n' }, false );
365+ private static final ByteList NEW_LINE = ByteList . create ( " \n " );
366366
367367 protected void writeHeaders (final RackResponseEnvironment response ) {
368368 this .headers .visitAll (currentContext (), new RubyHash .Visitor () { // headers.each { |key, val| }
@@ -386,7 +386,7 @@ public void visit(final IRubyObject key, final IRubyObject val) {
386386 } // else will do addHeader
387387 }
388388
389- if ( name .equals ("Transfer-Encoding" ) ) {
389+ if ( name .equalsIgnoreCase ("Transfer-Encoding" ) ) {
390390 if ( skipEncodingHeader (val ) ) return ;
391391 }
392392
@@ -566,9 +566,8 @@ public IRubyObject chunked_p(final ThreadContext context) {
566566 return context .runtime .newBoolean ( isChunked () );
567567 }
568568
569- private static final ByteList TRANSFER_ENCODING = new ByteList (
570- new byte [] { 'T' ,'r' ,'a' ,'n' ,'s' ,'f' ,'e' ,'r' ,'-' ,'E' ,'n' ,'c' ,'o' ,'d' ,'i' ,'n' ,'g' },
571- false );
569+ private static final ByteList TRANSFER_ENCODING = ByteList .create ("Transfer-Encoding" );
570+ private static final ByteList TRANSFER_ENCODING_LOWER = ByteList .create ("transfer-encoding" );
572571
573572 private Boolean chunked ;
574573
@@ -578,15 +577,26 @@ public IRubyObject chunked_p(final ThreadContext context) {
578577 public boolean isChunked () {
579578 if ( chunked != null ) return chunked ;
580579 if ( this .headers != null ) {
581- final RubyString key = RubyString .newString (getRuntime (), TRANSFER_ENCODING );
582- final IRubyObject value = this .headers .callMethod ("[]" , key );
580+ final IRubyObject value = getHeaderValue (TRANSFER_ENCODING , TRANSFER_ENCODING_LOWER );
583581 if ( value instanceof RubyString ) {
584582 return chunked = ( (RubyString ) value ).getByteList ().equal (CHUNKED );
585583 }
586584 }
587585 return chunked = Boolean .FALSE ;
588586 }
589587
588+ /**
589+ * Rack does not mandate response header name casing - apps might use the
590+ * conventional Capitalized-Names or (Rack 3.x style) lower-case names.
591+ */
592+ private IRubyObject getHeaderValue (final ByteList canonicalName , final ByteList lowerCaseName ) {
593+ IRubyObject value = this .headers .callMethod ("[]" , RubyString .newString (getRuntime (), canonicalName ));
594+ if ( value .isNil () ) {
595+ value = this .headers .callMethod ("[]" , RubyString .newString (getRuntime (), lowerCaseName ));
596+ }
597+ return value ;
598+ }
599+
590600 /**
591601 * @return whether de-chunking (a chunked Rack response) should be performed
592602 */
@@ -599,19 +609,17 @@ public IRubyObject flush_p(final ThreadContext context) {
599609 return context .runtime .newBoolean ( doFlush () );
600610 }
601611
602- private static final ByteList CONTENT_LENGTH = new ByteList (
603- new byte [] { 'C' ,'o' ,'n' ,'t' ,'e' ,'n' ,'t' ,'-' ,'L' ,'e' ,'n' ,'g' ,'t' ,'h' },
604- false );
612+ private static final ByteList CONTENT_LENGTH = ByteList .create ("Content-Length" );
613+ private static final ByteList CONTENT_LENGTH_LOWER = ByteList .create ("content-length" );
605614
606615 /**
607616 * @return whether output (body) should be flushed after each written line
608617 */
609618 protected boolean doFlush () {
610619 if ( isChunked () ) return true ;
611620 if ( this .headers != null ) {
612- final RubyString key = RubyString .newString (getRuntime (), CONTENT_LENGTH );
613- final IRubyObject value = this .headers .callMethod ("[]" , key );
614- return value .isNil (); // does not have a Content-Length header
621+ // does not have a Content-Length header :
622+ return getHeaderValue (CONTENT_LENGTH , CONTENT_LENGTH_LOWER ).isNil ();
615623 }
616624 return false ;
617625 }
@@ -645,7 +653,7 @@ protected boolean isClientAbortException(final Exception ioe) {
645653 return false ;
646654 }
647655
648- private static final ByteList CHUNKED = new ByteList ( new byte [] { 'c' , 'h' , 'u' , 'n' , 'k' , 'e' , 'd' }, false );
656+ private static final ByteList CHUNKED = ByteList . create ( "chunked" );
649657
650658 private boolean skipEncodingHeader (final IRubyObject value ) {
651659 if ( dechunk == Boolean .FALSE ) return false ;
0 commit comments