New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add early hints feature #1403

Merged
merged 1 commit into from Oct 4, 2017

Conversation

Projects
None yet
5 participants
@eileencodes
Contributor

eileencodes commented Aug 29, 2017

Worked with @tenderlove on adding early hints. This is still a work in progress as there are a few things we need to address, but this PR is to start the conversation.

This commit adds the early hints lambda to the headers hash. Users can
call it to emit the early hints headers. For example:

class Server
  def call env
    if env["REQUEST_PATH"] == "/"
      env['rack.early_hints'].call([{ link: "/style.css", as: "style" }, { link: "/script.js" }])
      [200, { "X-Hello" => "World" }, ["Hello world!"]]
    else
      [200, { "X-Hello" => "World" }, ["NEAT!"]]
    end
  end
end

run Server.new

In this example, the server sends stylesheet and javascript early hints
if the proxy supports it, it will send H2 pushes to the client.

Of course not every proxy server supports early hints, so to enable the
early hints feature with puma you have to pass the configuration variable,
--early-hints.

@tenderlove

This comment has been minimized.

Show comment
Hide comment
@tenderlove

tenderlove Aug 29, 2017

@eileencodes and I are working on pushing early hints support in to the Rack spec for version 2 (version 2 of the SPEC, not version 2 of the gem). I'd like to try experimentally implementing this in Puma first, then push support to Rails, then upstream to the Rack SPEC. IOW, I don't want this to be done in a vacuum.

Does this seem like an acceptable approach for the Puma team? I can understand if you don't want to be used for experimentation, but I would like to get this in to production systems sooner rather than later. 😊

Also, we've verified this to work with Puma + h2o.

tenderlove commented Aug 29, 2017

@eileencodes and I are working on pushing early hints support in to the Rack spec for version 2 (version 2 of the SPEC, not version 2 of the gem). I'd like to try experimentally implementing this in Puma first, then push support to Rails, then upstream to the Rack SPEC. IOW, I don't want this to be done in a vacuum.

Does this seem like an acceptable approach for the Puma team? I can understand if you don't want to be used for experimentation, but I would like to get this in to production systems sooner rather than later. 😊

Also, we've verified this to work with Puma + h2o.

@tenderlove

This comment has been minimized.

Show comment
Hide comment
@tenderlove

tenderlove Aug 29, 2017

I think we're using Ruby 2.3+ specific features in the test in this PR. If the overall concept seems OK, then we'll fix it up.

tenderlove commented Aug 29, 2017

I think we're using Ruby 2.3+ specific features in the test in this PR. If the overall concept seems OK, then we'll fix it up.

@nateberkopec

This comment has been minimized.

Show comment
Hide comment
@nateberkopec

nateberkopec Aug 29, 2017

Member

👏 Give me a while to look at it, headed to Japan soon so not sure how much time I'll have. Last resort we can talk about it at Kaigi?

Member

nateberkopec commented Aug 29, 2017

👏 Give me a while to look at it, headed to Japan soon so not sure how much time I'll have. Last resort we can talk about it at Kaigi?

@tenderlove

This comment has been minimized.

Show comment
Hide comment
@tenderlove

tenderlove commented Aug 29, 2017

@nateberkopec sounds good. 😊

@nateberkopec

This comment has been minimized.

Show comment
Hide comment
@nateberkopec

nateberkopec Aug 29, 2017

Member

I can understand if you don't want to be used for experimentation, but I would like to get this in to production systems sooner rather than later.

also I think I speak for the whole Puma team when I say that we have no problem with experimentation. hell Evan wanted to add native websockets and rewrite the entire reactor. so adding an optional feature...pretty tame 😆

Member

nateberkopec commented Aug 29, 2017

I can understand if you don't want to be used for experimentation, but I would like to get this in to production systems sooner rather than later.

also I think I speak for the whole Puma team when I say that we have no problem with experimentation. hell Evan wanted to add native websockets and rewrite the entire reactor. so adding an optional feature...pretty tame 😆

@nateberkopec

Couple of things just for "Puma style" concerns.

Show outdated Hide outdated lib/puma/server.rb Outdated
Show outdated Hide outdated lib/puma/server.rb Outdated
Show outdated Hide outdated lib/puma/dsl.rb Outdated
Show outdated Hide outdated lib/puma/server.rb Outdated
Show outdated Hide outdated lib/puma/server.rb Outdated
Show outdated Hide outdated test/test_puma_server.rb Outdated

@nateberkopec nateberkopec self-assigned this Aug 29, 2017

@tenderlove

This comment has been minimized.

Show comment
Hide comment
@tenderlove

tenderlove commented Sep 7, 2017

@nateberkopec

This comment has been minimized.

Show comment
Hide comment
@nateberkopec

nateberkopec Sep 22, 2017

Member

Need to look at this one more time when I'm not jetlagged but I think this is ready.

Member

nateberkopec commented Sep 22, 2017

Need to look at this one more time when I'm not jetlagged but I think this is ready.

@eileencodes eileencodes changed the title from WIP: Add early hints feature to Add early hints feature Sep 23, 2017

@tenderlove

This comment has been minimized.

Show comment
Hide comment
@tenderlove

tenderlove Sep 28, 2017

Hey folks. I've been talking to @matthewd about this feature. He convinced me that the lambda should take a header hash just like the header hash from a rack response. This would alleviate webservers from knowing how the Link header is formatted, and may allow us to send other headers (like X- headers) for debugging purposes.

IOW, we should change the API to be something like this:

env[EARLY_HINTS].call(“Link”: [“<...>; rel=preload; as=..”, “..”])

WDYT?

tenderlove commented Sep 28, 2017

Hey folks. I've been talking to @matthewd about this feature. He convinced me that the lambda should take a header hash just like the header hash from a rack response. This would alleviate webservers from knowing how the Link header is formatted, and may allow us to send other headers (like X- headers) for debugging purposes.

IOW, we should change the API to be something like this:

env[EARLY_HINTS].call(“Link”: [“<...>; rel=preload; as=..”, “..”])

WDYT?

@nateberkopec

This comment has been minimized.

Show comment
Hide comment
@nateberkopec

nateberkopec Oct 3, 2017

Member

@tenderlove If I'm reading the HTTP 103 spec correctly:

A server MUST NOT include Content-Length, Transfer-Encoding, or any
hop-by-hop headers ([RFC7230], section 6.1) in the informational
response using the status code.
A client MAY speculatively evaluate the headers included in the
informational response while waiting for the final response. For
example, a client may recognize the link header of type preload and
start fetching the resource. However, the evaluation MUST NOT affect
how the final response is processed; the client must behave as if it
had not seen the informational response.

So, 103 status CANNOT contain a Connection, Content-Length or Transfer-Encoding header, but MAY contain any other header?

If I'm getting that right, your change makes sense. But should anyone be responsible for omitting the prohibited headers? Puma's job or someone elses?

Member

nateberkopec commented Oct 3, 2017

@tenderlove If I'm reading the HTTP 103 spec correctly:

A server MUST NOT include Content-Length, Transfer-Encoding, or any
hop-by-hop headers ([RFC7230], section 6.1) in the informational
response using the status code.
A client MAY speculatively evaluate the headers included in the
informational response while waiting for the final response. For
example, a client may recognize the link header of type preload and
start fetching the resource. However, the evaluation MUST NOT affect
how the final response is processed; the client must behave as if it
had not seen the informational response.

So, 103 status CANNOT contain a Connection, Content-Length or Transfer-Encoding header, but MAY contain any other header?

If I'm getting that right, your change makes sense. But should anyone be responsible for omitting the prohibited headers? Puma's job or someone elses?

@tenderlove

This comment has been minimized.

Show comment
Hide comment
@tenderlove

tenderlove Oct 3, 2017

If I'm getting that right, your change makes sense. But should anyone be responsible for omitting the prohibited headers? Puma's job or someone elses?

I think if you wanted to omit them in Puma it would be fine. However, I'd expect the proxy to complain or raise an error if it got those headers, so maybe Puma shouldn't care? It would be nice for the user to get an exception closer to where the error is occurring, but I don't think it's necessary.

tenderlove commented Oct 3, 2017

If I'm getting that right, your change makes sense. But should anyone be responsible for omitting the prohibited headers? Puma's job or someone elses?

I think if you wanted to omit them in Puma it would be fine. However, I'd expect the proxy to complain or raise an error if it got those headers, so maybe Puma shouldn't care? It would be nice for the user to get an exception closer to where the error is occurring, but I don't think it's necessary.

Add early hints feature
This commit adds the early hints lambda to the headers hash. Users can
call it to emit the early hints headers. For example:

```
class Server
  def call env
    if env["REQUEST_PATH"] == "/"
      env['rack.early_hints'].call("Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload")
      [200, { "X-Hello" => "World" }, ["Hello world!"]]
    else
      [200, { "X-Hello" => "World" }, ["NEAT!"]]
    end
  end
end

run Server.new
```

In this example, the server sends stylesheet and javascript early hints
if the proxy supports it, it will send H2 pushes to the client.

Of course not every proxy server supports early hints, so to enable the
early hints feature with puma you have to pass the configuration variable,
`--early-hints`.

If `ENV['rack.early_hints']` is not set then early hints is not
supported by the webserver. Early hints is off by default.
@eileencodes

This comment has been minimized.

Show comment
Hide comment
@eileencodes

eileencodes Oct 3, 2017

Contributor

I've updated the PR so that now the lambda now takes a header hash and tested this in the Rails implementation for our test app. Let me know if you'd like other changes but I think this is good to go now.

Contributor

eileencodes commented Oct 3, 2017

I've updated the PR so that now the lambda now takes a header hash and tested this in the Rails implementation for our test app. Let me know if you'd like other changes but I think this is good to go now.

@nateberkopec nateberkopec merged commit 0169974 into puma:master Oct 4, 2017

2 checks passed

continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

eileencodes added a commit to eileencodes/rails that referenced this pull request Oct 4, 2017

Implement H2 Early Hints for Rails
When puma/puma#1403 is merged Puma will support the Early Hints status
code for sending assets before a request has finished.

While the Early Hints spec is still in draft, this PR prepares Rails to
allowing this status code.

If the proxy server supports Early Hints, it will send H2 pushes to the
client.

This PR adds a method for setting Early Hints Link headers via Rails,
and also automatically sends Early Hints if supported from the
`stylesheet_link_tag` and the `javascript_include_tag`.

Once puma supports Early Hints the `--early-hints` argument can be
passed to the server to enable this or set in the puma config with
`early_hints(true)`. Note that for Early Hints to work
in the browser the requirements are 1) a proxy that can handle H2,
and 2) HTTPS.

To start the server with Early Hints enabled pass `--early-hints` to
`rails s`.

This has been verified to work with h2o, Puma, and Rails with Chrome.

The commit adds a new option to the rails server to enable early hints
for Puma.

Early Hints spec:
https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-04

[Eileen M. Uchitelle, Aaron Patterson]

khall added a commit to khall/rails that referenced this pull request Oct 4, 2017

Implement H2 Early Hints for Rails
When puma/puma#1403 is merged Puma will support the Early Hints status
code for sending assets before a request has finished.

While the Early Hints spec is still in draft, this PR prepares Rails to
allowing this status code.

If the proxy server supports Early Hints, it will send H2 pushes to the
client.

This PR adds a method for setting Early Hints Link headers via Rails,
and also automatically sends Early Hints if supported from the
`stylesheet_link_tag` and the `javascript_include_tag`.

Once puma supports Early Hints the `--early-hints` argument can be
passed to the server to enable this or set in the puma config with
`early_hints(true)`. Note that for Early Hints to work
in the browser the requirements are 1) a proxy that can handle H2,
and 2) HTTPS.

To start the server with Early Hints enabled pass `--early-hints` to
`rails s`.

This has been verified to work with h2o, Puma, and Rails with Chrome.

The commit adds a new option to the rails server to enable early hints
for Puma.

Early Hints spec:
https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-04

[Eileen M. Uchitelle, Aaron Patterson]
@jumph4x

This comment has been minimized.

Show comment
Hide comment
@jumph4x

jumph4x Oct 5, 2017

ilu guys

jumph4x commented Oct 5, 2017

ilu guys

@nateberkopec nateberkopec referenced this pull request Oct 16, 2017

Closed

HTTP 2.0 #454

fast_write client, "#{k}: #{v}\r\n"
end
else
fast_write client, "#{k}: #{v}\r\n"

This comment has been minimized.

@PikachuEXE

PikachuEXE Mar 23, 2018

Should this be fast_write client, "#{k}: #{vs}\r\n" instead?
I don't see v defined in this level

@PikachuEXE

PikachuEXE Mar 23, 2018

Should this be fast_write client, "#{k}: #{vs}\r\n" instead?
I don't see v defined in this level

This comment has been minimized.

@eileencodes

eileencodes Mar 28, 2018

Contributor

This comment has been minimized.

@eileencodes

eileencodes Mar 28, 2018

Contributor

Oh i see what you're saying - I will fix it

@eileencodes

eileencodes Mar 28, 2018

Contributor

Oh i see what you're saying - I will fix it

PikachuEXE added a commit to PikachuEXE/passenger that referenced this pull request Apr 4, 2018

PikachuEXE added a commit to PikachuEXE/passenger that referenced this pull request Apr 4, 2018

CamJN added a commit to phusion/passenger that referenced this pull request May 7, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment