middleman: Build failure with `asset_host` config and rack >=2.1.0 with `Content-Length header` error

Expected behavior and actual behavior

middleman build succeeds

Steps to reproduce the problem (from a clean middleman installation)

  1. gem install middleman
  2. middleman init
  3. add activate :asset_host, host: 'http://assets.example.com' to config.rb
  4. middleman build --verbose

Observe build fails.

Additionally, if you downgrade rack you can observe that the build will succeed:

  1. rm -f Gemfile.lock
  2. echo "gem 'rack', '2.0.8'" >> Gemfile
  3. middleman build --verbose

Observe the build succeeds

Additional information

  • Ruby version: ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin18]
  • Middleman version: 4.3.5
  • OS version: Mac OS 10.14.6

Code example:

https://github.com/lsirivong/middleman-example (build fails on master branch) https://github.com/lsirivong/middleman-example/tree/rack-2.0.8 (build succeeding on this branch)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 3
  • Comments: 18 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Released as v4.3.6

Me and my colleague have investigated the issue and the culprit seems to be in the asset hash and asset host rewriting. Basically, the metaprogrammed method called #rewrite_inline_urls (which was very hard to find the definition of) does change the contents of resources, but does not change the precomputed Content-Length headers for them. We were not able to find how the result of the rewriting feeds back into the Rack response pipeline, so we opted for adding the following to our config.rb for Middleman to fix it:

class ZapContentLength < Struct.new(:app)
  def call(env)
    s, h, b = app.call(env)
    # The URL rewriters in Middleman do not update Content-Length correctly,
    # which makes Rack-Lint flag the responses as having a wrong Content-Length.
    # For building assets this has zero importance because the Content-Length
    # header will be discarded - it is the server that recomputes it. But
    # it does prevent the site from building correctly.
    #
    # The fastest way out of this is to let Rack recompute the Content-Length
    # forcibly, for every response, at retrieval time.
    #
    # See https://github.com/middleman/middleman/issues/2309
    # and https://github.com/rack/rack/issues/1472
    h.delete('Content-Length')
    [s, h, b]
  end
end

app.use ::Rack::ContentLength
app.use ZapContentLength

This forces recomputation of the Content-Length header leading to the correct result. I had serious trouble navigating the Contracts-based inheritance chain for the rewriters, so I hope someone more knowledgeable with Middleman internals can address this.

I’ve put up a patch in https://github.com/middleman/middleman/pull/2316 - can someone in thread test it? I do not have time to do so at the moment but I believe it should be sufficient.

Is this fixed?

Oh my word, @julik, thank you for this Content Length fix. I felt like I was going insane.

For further context / for anyone else searching for this bug for CSS (not Rack) reasons: This bug is also tripped by combining relative_assets with even one instance of image-url() in your styles. This bug prevents the CSS file from being generated.

For me, downgrading to 2.0 from 2.1 wasn’t enough. I had to lock the version to 2.0.8 to resolve the content-length errors I was getting.