The mirror module is exciting. Sometimes it's nice to have no-consequences testing of production traffic in a staging environment. Unless you have something like Envoy and its shadowing functionality [0] to handle the mirroring, you end up using a tool like GoReplay [1] to duplicate the traffic to another environment and ignore the responses. This looks like a cleaner and simpler way to accomplish the task.
It appears that the default build does not check for libc support of Full RELRO and PIE. Are there any plans to add checks for this, or is it assumed that everyone sets the right CFLAGS and LDFLAGS? I know that Debian, Ubuntu, Gentoo, Alpine and Fedora package build specs do this by default today.
The reason I ask is that I see a lot of people build this themselves and run it from docker. I am concerned that they are not getting the various libc protections that should be enabled on internet facing daemons. i.e. stack-protector, fortify source, full relro, pie, ssp buffer limits, etc..
I forgot to mention, if you want to check your existing daemons for these protections, either apt/yum install "checksec", or grab the script from it's maintainer [1] to check running daemons or files.
Silly question, but what's the use case for the HTTP/2 Push? Their example with pushing doesn't make sense to me. Why would you want to push static content?
In 2016, the Chromium team at Google produced a document [1] that examines usecases for HTTP/2 Push, talks about deployment models, and analyzes whether it's worth it. In this particular case, you'd push static content because you know it will be needed later, and this way the information arrives in the HTTP header instead of in the payload's content body, so by the time 'main.css' is needed, the UA's HTTP cache may already be populated with the file.
That being said, I fail to see how in the general case, setting static headers in the server software's config for Push is useful [2][3], and wish that more implementations converged on a common way of describing what to push [4], so that tools could be built around discovering dependencies, and around interpreting that manifest to execute push.
Pushes are probably best implemented in a caching layer, not manually describing what to push. A web server should not just cache resources, but also learn what kind of resources are often requested with each page and just push those next time someone makes a request. And some sort of push prediction policy should be configurable.
It's not sensible for pushes to be implemented in a caching layer, because pushes are effectively the manual overrides to the User-Agent's own caching; conversely, the User-Agent's cache is perfectly appropriate as a cache, and doesn't need HTTP/2 Push to work. HTTP/2 Push is effectively the server declaring they know better, so they prime the UA's cache to avoid additional roundtrips.
Nginx does have a module [1] and a corresponding configuration option to scan outgoing headers for Link header preload directives, and once it has learned of a preload being declared by a resource, it will push that resource thereafter. Nginx talks about the justification for this feature, where they too admit that statically configuring pushes in the server config is not terribly useful -- it's quite often the wrong place to specify relationships between resources.
If you can predict with high enough accuracy what resource is going to be requested by the client next, I don't see why pushing it would be a bad idea. Speculation is how we hide latency after all.
And if you think about it, static pushes in general have very limited usefulness, almost non existent. Imagine when some url becomes popular and almost all of the requests to that url come from people who never visited the website before. It would make sense for a web server to learn what kind of resources clients request with that url and start pushing those resources to people ahead of time.
For that it's easier to parse the pushed content. If it's HTML, then catch stlyesheets, JS, and some other static <img src=.../> things. It doesn't have to be flawless, after all it's just a speed-up. (And if you want a speed up write nice markup.)
Similarly, it should be the backend behind the reverse-proxy that knows what's the page that has been just rendered, and knows about the user's session (is it brand new, or maybe it's not new, but still needs to push things because it's too old, and since then that particular page's background changed, etc.).
And in case of an Angular/React/SPA thing, then the "bundler/compiler" should create a list of things to push for various URLs. Or the Angular/React team should talk with the Nginx team to figure out how to speed up things. (In case of SSR - server side rendering - the NodeJS server can emit the necessary Link headers, for example.)
Gathering stats requires keeping them somewhere. Making inferences. Documenting the inference engine. Explaining the magic to users. Sounds a lot more complicated than explaining that what HTML tags will be parsed.
Proxies are already complicated as is. Caching proxies more so. (Think of how Varnish has a - probably Turing complete - DSL to decide what to serve and/or cache and when, and how.)
Parsing HTML content won't get you the full benefit an inference engine would. An inference engine could easily learn that 90% of your users getting to your landing page are going to login & end up on their home screen so it would push the static resources for the home screen too. Similarly, it might know that it already pushed those resources previously in the session & only push the new static resources that are unique to you once you login (saving the round-trip of the client nacking the resource). Doing it via stateless HTML parsing is never going to work because you have no idea of the state of the session. That doesn't mean there's not a place for a mixture of approaches (& yes you could teach the HTML parsing about historical pushes but then you get back to the concern you raised about storing that data somewhere).
The HTML parsing approach is probably great from a 80% of the benefit for 20% of the effort on small-scale websites (i.e. majority). A super accurate inference engine might use deep learning to train what to serve on a very personalized level if you have a lot of users & the CPU/latency trade-off makes sense for your business model (i.e. more accuracy for a larger slice of your population). A less accurate one might just collect statistics in a DB & make cheap less accurate guesses from that (or use more "classic ML" like Bayes) if you have a medium amount of users or the CPU usage makes more sense and you're OK with the maintenance burden of a DB. It's a sliding scale IMO of tradeoffs with different approaches making sense depending on your priorities.
Yes, I agree, that of course a hypothetical ML/AI outperforms any naive and simple solution. But usually magic technology is required to do that, otherwise it wouldn't be magic :)
That said a simple heuristic like "after requesting an URL the server got these requests on the same HTTP/2 connection in less than 1 second, and those were static assets served with Expires headers" could work.
> It's not sensible for pushes to be implemented in a caching layer, because pushes are effectively the manual overrides to the User-Agent's own caching; conversely, the User-Agent's cache is perfectly appropriate as a cache, and doesn't need HTTP/2 Push to work.
By "caching layer" he probably meant a proxy or load balancer level cache not the user agent cache. It would make total sense for a load balancer to statistically discover relationships.
Once you have a config setting, you've done all the work to actually get Push support, which is the hard part. Support for reading a manifest can be added later, or other people can write tools to read manifests and generate config files for the server.
Let's say you have an HTML page which links to main.css. Ordinarily, the request goes:
Client: GET /index.html
Server: <index.html>
Client (after parsing): GET /main.css
Server: <main.css>
Loading the page thus takes 2 round trips, one for the main page and one for the content. (Or more, if you have e. g. includes in the CSS.) Here's what it would look like with HTTP/2 Push:
Client: GET /index.html
Server: <index.html>, <main.css> (PUSH)
This only takes 1 round trip; since the server knows that main.css will be required shortly it can preemptively send it. In particular, this might offer a significant speedup for high-latency connections; it also theoretically reduces the need for bundling tools since you can have the server just push all of the individual files.
The obvious problem with this scheme is that if the client already has main.css then it's a waste of bandwidth to send it again. The client can cancel the push, but by the time it finds out about it a bunch of data has already been sent. There is a proposal for 'Cache Digests' which will allow the client to send a Bloom filter of its cache so the server can tell whether or not it has the file already, but as far as I'm aware no major client or server has implemented this yet.
I think there's a common misconception with the term "push". HTTP2 doesn't push in terms of a push notification, but rather "pushes" assets down the connection that are known to be needed by the currently transferred document (whatever that may be).
That way, the web server can pro-actively push the named stylesheet to the client as it knows that the stylesheet is needed to render the page. That way the client doesn't have to ask the server (which would result in a new roundtrip).
"that are known to be needed by the currently transferred document"
How does the server knows what the browser/client "needs" ? The client can have the cached stylesheet already. Making the server "in control" seems wrong and make things even more complicated.
That's the thing, it doesn't. HTTP2 Push is one of the big "open field" of HTTP2, and to know whether a document needs to be pushed will rely on good heuristics and black magic.
There is however a small standard that's emerging, pioneered by h2o, called casper (https://h2o.examp1e.net/configure/http2_directives.html#http...). The idea is that all resources ever sent to the client are stored in a probabilistic data structure in a cookie. On every request the structure is sent back to the server, which can then check whether the resource has good chances to be already known by the browser.
The client can cancel the push. But yes, there's definitely wasted bandwidth here - the reason to still do it is that connections are now fast enough that the extra download time is small compared to the time required to parse HTML/send new HTTP request/receive response/render CSS.
I'm more concerned with a more "traditional" setup - say a festival providing WiFi to many people through limited upstream. Used to be, you could provide a caching proxy locally.
With the war on mitm, it's really hard to set up something that scales traffic in this way - even if the actual data requested by clients could readily scale.
I know it's a trade-off between security and features - but it still makes me sad.
It's 2G. By the time the cancel is received by the server, the server will have sent the resource, the bytes will have traveled and the user will be billed.
You imply that there is a delay between the promise and the push, but it is not necessarily so. In fact the promise and the data may be sent in the same packet.
There's a few interesting things here that I want to point out:
* "A client can request that server push be disabled" This is part an explicit parameter in the client request to a server for anything, https://http2.github.io/http2-spec/#SETTINGS_ENABLE_PUSH.
* "Pushed responses that are cacheable (see [RFC7234], Section 3) can be stored by the client, if it implements an HTTP cache. Pushed responses are considered successfully validated on the origin server (e.g., if the "no-cache" cache response directive is present ([RFC7234], Section 5.2.2)) while the stream identified by the promised stream ID is still open"
Note that pushed content first starts with a PUSH_PROMISE message to the client, which the client can decide on its own volition to reject. Note the spec for a PUSH_PROMISE frame is here, https://http2.github.io/http2-spec/#PUSH_PROMISE and it's extremely small. Even on 2G or dial-up it's by design negligible.
* "Once a client receives a PUSH_PROMISE frame and chooses to accept the pushed response, the client SHOULD NOT issue any requests for the promised response until after the promised stream has closed.
If the client determines, for any reason, that it does not wish to receive the pushed response from the server or if the server takes too long to begin sending the promised response, the client can send a RST_STREAM frame, using either the CANCEL or REFUSED_STREAM code and referencing the pushed stream's identifier. "
Wittingly or otherwise, your message comes across as "everyone on the standards boards are idiots, don't think about anything beyond the valley, and I'm smarter than they are." That's beyond ridiculous. The standard was designed by subject matter experts from right across the world, with interests in web technologies across all sorts of markets, including the developing nations where every single byte is important. There's a lot that has been designed in to the HTTP 2.0 specification to account for that and to explicitly try to improve end user experience under those conditions.
The server doesn't send the data every time. First it sends a data frame letting the client know "hey, I've got this thing if you need it" and the browser can respond with a frame saying "nah, don't need it".
The danger here is that you push too much, but the actual response will still be delivered almost as fast (due to non-blocking behavior in HTTP/2 connections), so sure, it's not optimal, but there are a lot of use cases besides static assets where it is very useful.
That command probably goes in a "location" block that matches the set of HTML pages that use main.css.
Normally, the browser parses HTML, finds a <link> tag that mentions main.css, and then requests main.css. With HTTP/2 push, by the time the browser has finished parsing the <link> tag, main.css has already been delivered.
If the browser already has main.css in its cache, it can reject the push.
If you agree that using a CDN for static content is a good idea, then it would seem HTTP/2 Push is useless.
The website is served from your servers while the static content is served from a CDN so you can't "push" it in the same stream as you webpage content.
Am I missing something here?
Yes, you can't push cross-origin.[1] However, there's still a lot of use-cases where this is useful, such as if your entire site is static content, or if your app servers are behind the CDN as well.
[1]: Yet. I believe the web packaging standard (intended, among other things, to replace AMP) allows pushing bundles signed by other origins.
That's anything but "simple" - you might want to reuse those stylesheets/scripts on other pages as well, for example; if you inline them into HTML, you're now wasting far more bandwidth, as you're unconditionally pushing them with every HTML response.
Caddy's binary distribution is closed-license, so beware. If you need it to be truly free, compile it from source. I did like Caddy's simple configuration format, though.
Nginx struggles at basic stuff like load balancing to microservice backends because of trivial stuff like DNS caching when running inside container orchestration platforms
With the new ingress in Kubernetes & lets encrypt plugin, you probably do not need nginx anymore, if you're adopting containers. In fact, it can be a hindrance to adopting container orchestration systems.
If you are aspiring to writing cloud native applications, there is not very compelling reasons to run reverse proxies in my opinion. If its possible to offload that responsibility to the cloud platform vs running your own infrastructure, that is highly desirable for some people.
In particular, I highly recommend checking out Envoy and Linkerd, which enable service mesh architectures and can take the place of HAProxy, Nginx, etc.
gRPC support is very welcome! I've used gRPC internally but have felt a bit uncomfortable exposing a server directly to the internet for outside client use. Not to mention difficulties deploying in a downtime-free manner.
gRPC + TLS in nginx will allow connections from outside that I'm comfortable with. Great improvement!
It's because Nginx uses even numbers for stable releases (very few changes) and odd versions for mainline releases (production-ready, but frequently improved/changed). 1.14.0 is the exact same version of 1.13.12.
> nginx-1.14.0 stable version has been released, incorporating new features and bug fixes from the 1.13.x mainline branch - including the mirror module, HTTP/2 push, the gRPC proxy module, and more.
gRpc support is amazing! I just wish this had been announced a week earlier, as I just spent a good amount of development time creating a service that subverts our NGINX server to create gRpc connections to desired microservices. It will be nice to just target the service by name directly instead of having to query Consul for their host IP addresses and ports.
[0] https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/ro... [1] https://goreplay.org/