Skip to content
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

HTTP/2: Server Push support #4249

Closed
Saphirim opened this issue Nov 27, 2018 · 36 comments
Closed

HTTP/2: Server Push support #4249

Saphirim opened this issue Nov 27, 2018 · 36 comments
Labels
affected-few This issue impacts only small number of customers area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions enhancement This issue represents an ask for new feature or an enhancement to an existing one severity-major This label is used by an internal tool
Milestone

Comments

@Saphirim
Copy link

Saphirim commented Nov 27, 2018

Support the HTTP/2 Server Push feature with the now previewed and for .Net Core 2.2 most likely GA HTTP/2 implementation.

HTTP/2 Server Push allows an HTTP/2-compliant server to send resources to a HTTP/2-compliant client before the client requests them. It is, for the most part, a performance technique that can be helpful in loading resources pre-emptively.

@davidfowl davidfowl transferred this issue from aspnet/KestrelHttpServer Nov 27, 2018
@Tratcher
Copy link
Member

No, server push will not be available in any AspNetCore servers for 2.2. We're still trying to gauge interest for 3.0.

@Saphirim
Copy link
Author

Interest here 😊 thx for your fast reply.

@tpeczek
Copy link

tpeczek commented Nov 27, 2018

Same here.

@CShepartd
Copy link

If not 2.2 then 3.0 will be nice

@asbjornu
Copy link
Member

asbjornu commented Feb 1, 2019

🙋🏼‍♂️ I would love to have the ability to push resources over an established HTTP/2 connection with ASP.NET.

@davidfowl
Copy link
Member

It would help if you can detail how you think you would take advantage of this feature in real applications. Code samples help as well.

@asbjornu
Copy link
Member

asbjornu commented Feb 1, 2019

I would like to use HTTP/2 Server Push to push related resources to a client; typically when a “collection” resource has been requested, each “item” resource in the collection could be pushed alongside the “collection” resource so they are available once the client starts iterating over the collection to fetch the individual items.

The alternative (and imho. sub-par) approach is to compound (“embed”, “transclude”, “include”, “expand”, etc.) all items within the collection resource, making caching of the collection resource hard and of the item resources impossible.

The relationship between the resources can also be seen as “parent” and “child”, “root” and “subresource”, etc., but the requirements are similar regardless of the relationship between the resources. Server push could provide helpful in all cases.

@davidfowl
Copy link
Member

How does that concretely change what you’re doing today? What are you doing today? What big downsides are you seeing and what would you expect the big upside to be.

I’m looking for something very concrete and not abstract, that’ll help us think about how to prioritize and even implement the feature.

@asbjornu
Copy link
Member

asbjornu commented Feb 4, 2019

@davidfowl, sure. Examples of how we (as an industry) deal with the problem of transclusion over HTTP/1.1 today can be found in GraphQL and OData. In GraphQL, transclusion is expressed as sub-selection:

POST /query HTTP/1.1
Host: example.com
Content-Type: application/graphql

{
  hero {
    friends {
      name
      height
    }
  }
}
HTTP/1.1 200 OK 
Content-Type: application/json

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker",
          "height": 1.72
        },
        {
          "name": "Han Solo",
          "height": 1.85
        },
        {
          "name": "Leia Organa",
          "height": 1.54
        }
      ]
    }
  }
}

In OData, transclusion is expressed with the $expand keyword:

GET /heroes/r2-d2?$expand=friends($select=name,height) HTTP/1.1
Host: example.com
HTTP/1.1 200 OK 
Content-Type: application/json

{
  "hero": {
    "name": "R2-D2",
    "friends": [
      {
        "name": "Luke Skywalker",
        "height": 1.72
      },
      {
        "name": "Han Solo",
        "height": 1.85
      },
      {
        "name": "Leia Organa",
        "height": 1.54
      }
    ]
  }
}

Each friend in the above responses are most likely HTTP resources themselves. There's no information provided that allows REST's layered constraint to be used to allow intermediary caching, resource-level RBAC or other resource-specific optimizations, since every "sub-resource" is compounded into one large response.

With HTTP/2, we can use Server Push to deliver each hero as its own resource instead. Hypothetical example based on @evert's Prefer-Push draft:

GET /heroes/r2-d2 HTTP/2
Prefer-Push: friends
Host: example.com
HTTP/2 200 OK
Content-Type: application/json

{
  "hero": {
    "name": "R2-D2",
    "friends": [
      {
        "id": "https://example.com/heroes/luke-skywalker"
      },
      {
        "id": "https://example.com/heroes/han-solo"
      },
      {
        "id": "https://example.com/heroes/leia-organa"
      }
    ]
  }
}

Following this response, three PUSH_PROMISE frames will be sent by the server; one for each friend:

:path /heroes/luke-skywalker
:authority example.com
:path /heroes/han-solo
:authority example.com
:path /heroes/leia-organa
:authority example.com

Then three HEADER and DATA frames are sent by the server containing the headers and contents of the three hero resources:

Content-Type: application/json

{
  "hero": {
    "name": "Luke Skywalker",
    "height": 1.72,
    "friends": [
      {
        "id": "https://example.com/heroes/r2-d2"
      },
      {
        "id": "https://example.com/heroes/han-solo"
      },
      {
        "id": "https://example.com/heroes/leia-organa"
      }
    ]    
  }
}
Content-Type: application/json

{
  "hero": {
    "name": "Han Solo",
    "height": 1.85,
    "friends": [
      {
        "id": "https://example.com/heroes/r2-d2"
      },
      {
        "id": "https://example.com/heroes/luke-skywalker"
      },
      {
        "id": "https://example.com/heroes/leia-organa"
      }
    ]
  }
}
Content-Type: application/json

{
  "hero": {
    "name": "Leia Organa",
    "height": 1.54,
    "friends": [
      {
        "id": "https://example.com/heroes/r2-d2"
      },
      {
        "id": "https://example.com/heroes/luke-skywalker"
      },
      {
        "id": "https://example.com/heroes/han-solo"
      }
    ]
  }
}

Now that each hero has been pushed with its own URI, intermediaries can cache them, authorize them, individually on a per-resource basis. This also has two added bonuses:

  1. The initial R2-D2 response will be more cacheable by being less tailored to the initial request.
  2. The recursive and stack overflow-inducing nature of the friends list is mitigate by not compounding every friend inside every friend, but instead referring to them by their URI.

How we should signal pushing of resources from the client to the server is still up for discussion. preload and prefetch are both viable options that are being investigated. Regardless of the initiation mechanism, a way to do HTTP/2 Server Push in an ASP.NET Core application is needed.

@serialseb
Copy link

Same scenario as above, we currently preload those as soon as we get the headers, before we parse the body, so we have headers -> links -> download body (and download links in the background), and finally reassemble once parsing has been done for the request entity body. We lose quite a bit of time in that model, and we have to maintain client code and specific infrastructure for it, which is a pain. People don't like pain. It's a good model. We can't experiment further with push as a replacement because of the lack of implementation.

@Daniel15
Copy link
Contributor

Daniel15 commented Sep 23, 2019

For what it's worth, Nginx supports HTTP/2 push (see https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/), so if you're using Nginx as a reverse proxy in front of your ASP.NET service, you just have to enable the http2_push_preload option and send the relevant preload Link headers and Nginx will convert that into a HTTP/2 push for you.

@CShepartd
Copy link

CShepartd commented Sep 24, 2019

@Daniel15 This is bad replace for real HTTP/2 Server Push support cuz of:

NGINX intercepts this header and commences a server push of /style.css. The path in the Link header must be absolute – relative paths like ./style.css are not supported

Adding additional configs in Nginx for cover this isn't right answer

@pedroreys
Copy link

+1 for adding Server Push support. That way we could implement something like Vulcain directly instead of having to use their Gateway Server for the Server Push support.

@naveedahmed1
Copy link

Does. Net core 3.1 supports HTTP/2 Server push?

@davidfowl
Copy link
Member

No it does not

@naveedahmed1
Copy link

Should we expect its support in .Net Core any time soon?

@davidfowl
Copy link
Member

davidfowl commented Apr 1, 2020

No, we're not working on this feature. The cost is high and it isn't a priority for the current release.

@naveedahmed1
Copy link

How about .Net 5, any plans to consider it for next major release?

@davidfowl
Copy link
Member

.NET 5 is the currrent release, so do you mean .NET 6? Sure nothing has been planned for that as yet.

@naveedahmed1
Copy link

naveedahmed1 commented Apr 1, 2020

Thanks @davidfowl, I thought .Net Core 3.x is considered as current release.

HTTP/2 server push can have major effect on the performance of web applications since it can improve page load time by pushing the required resources in a single request. And now most of the browsers support this feature, don't you think its support should be added to one of the most popular web development platform?

Ref: https://developers.google.com/web/fundamentals/performance/http2#server_push

@Tratcher
Copy link
Member

Tratcher commented Apr 1, 2020

HTTP/2 server push can have major effect on the performance of web applications

These benefits are largely theoretical and come with a lot of issues of their own (e.g. cache busting). The various standards groups are still trying to make it work in practice.

@davidfowl
Copy link
Member

Sorry current release as in the one we are currently working on. We already have planned and have started working on .NET 5 for a few months now.

@ygoe
Copy link

ygoe commented Apr 28, 2020

I'd like to see push support to be able to send messages to a REST API client from the server. I've just started playing with HTTP2 but failed immediately and can't recover. So I went on and checked push support. Now that I see it's not there and not coming in the foreseeable future, I can give up on HTTP2 altogether. This means I need to find another solution to send events through a REST API (or something similar and lightweight).

PS. It's a local connection, so nothing over a network. Maybe classic .NET remoting would work here. 😆

@Tratcher
Copy link
Member

@ygoe HTTP/2 PUSH is not an event API, the client still has to request the resource from the HTTP stack. You should look at WebSockets, SignalR, etc..

@evert
Copy link

evert commented Apr 28, 2020

To pile on on to @tracychms , HTTP/2 Push is really a mechanism to 'warm the cache' and send responses to GET requests that the client will need to make soon anyway. It removes a potential round-trip. Still useful for speeding up APIs

@ygoe
Copy link

ygoe commented Apr 28, 2020

OK, must have misunderstood this. But hasn't there been such a message send thing somewhere around HTTP a few years ago? Or was that WebSocket only?

@evert
Copy link

evert commented Apr 28, 2020

Websocket enables server-initiated messaging to a browser, and it piggybacks on HTTP/1.1 to initiate the connection. Websocket over HTTP/2 will use HTTP/2 frames.

I think there was a lot of misunderstanding over what HTTP/2 Push meant. If it's helpful I wrote something about the use-case of HTTP/2 push in the context of APIs: https://evertpot.com/h2-parallelism/

@Tratcher Tratcher added affected-few This issue impacts only small number of customers enhancement This issue represents an ask for new feature or an enhancement to an existing one severity-major This label is used by an internal tool labels Oct 12, 2020 — with ASP.NET Core Issue Ranking
@ghost
Copy link

ghost commented Nov 4, 2020

Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@JamesNK
Copy link
Member

JamesNK commented Nov 8, 2020

For reference, System.Web API for this - https://docs.microsoft.com/en-us/dotnet/api/system.web.httpresponse.pushpromise?view=netframework-4.8

@davidfowl
Copy link
Member

davidfowl commented Nov 12, 2020

Further reference, this is being removed from chrome https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYLvmQUBY/m/vOWBKZGoAQAJ

Almost five and a half years after the publication of the HTTP/2 RFC, server push is still extremely rarely used. Over the past 28 days, 99.95% of HTTP/2 connections created by Chrome never received a pushed stream (Net.SpdyStreamsPushedPerSession), and 99.97% of connections never received a pushed stream that got matched with a request (Net.SpdyStreamsPushedAndClaimedPerSession). These numbers are exactly the same as in June 2019. In June 2018, 99.96% of HTTP/2 connections never received a pushed stream. These numbers indicate the lack of active efforts by server operators to increase push usage. On top of this, less than 40% of received pushes are used, down from 63.51% two years ago. The rest are invalid, never get matched to a request, or already in cache.

@asbjornu
Copy link
Member

asbjornu commented Nov 12, 2020

Thanks for sharing, @davidfowl. First I have to say that the Chrome developers are overly impatient. For complex enough concepts and technologies, 5 years is nothing. Two examples:

  1. Fielding wrote his REST thesis 20 years ago and most developers still have no idea what it's about.
  2. It took the concept of lambda expressions 71 years from its inception in 1936 before it was popularized and made accessible to regular (dark matter) developers in C# 3.0 in 2007.

Secondly, I believe server push to be much more useful with fine-grained control in API use cases than it has been in browsers. Browser support is thus not very relevant, at least not for the use cases I've envisioned.

@tpeczek
Copy link

tpeczek commented Nov 12, 2020

Hi @davidfowl,

It looks the intent is to pivot toward 103 Early Hints as Chrome has recently started an experiment to evaluate their effectiveness.

It looks like, similar to Server Push, this is not something which can be added on top of ASP.NET Core in .NET 5, unless I'm missing an extensibility point which allows emitting informational responses for HTTP/2.

Is this something which could be tracked/considered for .NET 6 (depending on that experiment results)?

@Tratcher
Copy link
Member

Yes 103 Early hints would require new API, please open a new issue for that. It would be pretty strait forward in Kestrel. This new feature isn't HTTP/2 specific, it should work with any version.

Note 1xx responses are so uncommon that to enable this on IIS/Http.Sys would require work in Http.Sys at the OS level. Only 100 Continue and 101 Switching Protocols are supported today, and those are both managed closely by Http.Sys, ASP.NET and IIS have minimal control over them.

@ygoe
Copy link

ygoe commented Nov 12, 2020

Two examples

Add to that IPv6. 25 years or so old and Vodafone still has no clue what that is or that it exists while other providers have almost switched off IPv4 a few years ago.

Glancing over the Google comment, it seems like Push is not very helpful but overly complex. I also had no intentions to make use of Push.

@tpeczek
Copy link

tpeczek commented Nov 14, 2020

@Tratcher I've created #27851

@shirhatti
Copy link
Contributor

Reposting @davidfowl's earlier comment from Chrome's intent to remove HTTP/2 server push

Almost five and a half years after the publication of the HTTP/2 RFC, server push is still extremely rarely used. Over the past 28 days, 99.95% of HTTP/2 connections created by Chrome never received a pushed stream (Net.SpdyStreamsPushedPerSession), and 99.97% of connections never received a pushed stream that got matched with a request (Net.SpdyStreamsPushedAndClaimedPerSession). These numbers are exactly the same as in June 2019. In June 2018, 99.96% of HTTP/2 connections never received a pushed stream. These numbers indicate the lack of active efforts by server operators to increase push usage. On top of this, less than 40% of received pushes are used, down from 63.51% two years ago. The rest are invalid, never get matched to a request, or already in cache.

Given that HTTP/2 server push was intended as a performance optimization and studies[1][2][3] have shown little to no performance improvements, the ASP.NET Core team has decided not do this.

[1]: https://github.com/httpwg/wg-materials/blob/gh-pages/ietf102/chrome_push.pdf
[2]: https://github.com/httpwg/wg-materials/blob/gh-pages/ietf102/akamai-server-push.pdf
[3]: https://medium.com/@ananner/http-2-server-push-performance-a-further-akamai-case-study-7a17573a3317

@ghost ghost locked as resolved and limited conversation to collaborators Dec 16, 2020
@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Aug 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affected-few This issue impacts only small number of customers area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions enhancement This issue represents an ask for new feature or an enhancement to an existing one severity-major This label is used by an internal tool
Projects
None yet
Development

No branches or pull requests