Couple of months ago, HTTP/2 was published as RFC7540.
How will this affect the existing REST API built on HTTP/1.1?
As per Wikipedia, HTTP/2 has added new features.
How can we take advantage of these new features?
The main semantic of HTTP has been retained in HTTP/2. This means that it still has HTTP methods such as GET, POST, etc., HTTP headers, and URIs to identify resources.
What has changed in HTTP/2 with respect to HTTP/1.1 is the way the HTTP semantic (e.g. "I want to PUT resource /foo on host example.com") is transported over the wire.
In this light, REST APIs built on HTTP/1.1 will continue to work transparently as before, with no changes to be made to applications. The web container that runs the applications will take care of translating the new wire format into the usual HTTP semantic on behalf of the applications, and application just see the higher level HTTP semantic, no matter if it was transported via HTTP/1.1 or HTTP/2 over the wire.
Because the HTTP/2 wire format is more efficient (in particular due to multiplexing and compression), REST APIs on top of HTTP/2 will also benefit of this.
The other major improvement present in HTTP/2, HTTP/2 Push, targets efficient download of correlated resources, and it's probably not useful in the REST usecase.
A typical requirement of HTTP/2 is to be deployed over TLS.
This require deployers to move from http to https, and setup the required infrastructure to support that (buy the certificates from a trusted authority, renew them, etc.).
HTTP/2 spec intentionally did not introduce new semantics for application programmer. In fact, major client-side libraries (NSUrlSession on iOS, OkHttp on Android, React Native, JS in browser environment) support HTTP/2 transparently to you as a developer.
Due to HTTP/2 ability to multiplex many requests over single TCP connection, some optimizations application developers implemented in the past, such as request batching would become obsolete and even counter-productive.
Push feature would likely be utilized to deliver events and will be able to replace polling and possibly websockets in some applications.
One possible application of server push feature in HTTP/2 to REST APIs is ability to accelerate legacy applications on i.e. reverse proxy level by pushing anticipated requests ahead of time to the client, instead of waiting for them to arrive. I.e. push answers to user profile, and other common API calls right after login request is complete.
However Push is not yet widely implemented across server and client infrastructure.
The main benefit I see is Server Push for hypermedia RESTful APIs, which hold references to resources, often absolute domain-dependent URLs such as /post/12.
Example: GET https://api.foo.bar/user/3
{
"_self": "/user/3"
"firstName": "John",
"lastName": "Doe",
"recentPosts": [
"/post/3",
"/post/13",
"/post/06
]
}
HTTP/2 Push can be used to preemptively populate the browser cache if the server knows the client will likely want to do certain GET requests in the future.
In the above example, if HTTP/2 Server Push is activated and properly configured, it could deliver /post/3, /post/13 and /post/06 along with /user/3.
Successive GETs to one of those posts would result in cached responses. Also, the cache digest draft would allow client to send information about the state of their cache, avoiding unnecessary pushes. This is much more practical for Hypermedia-driven APIs then embedded bodies such has does HAL.
More information on the reasons here: The problems with embedding in REST today and how it might be solved with HTTP/2.
Related
HTTP/1.x has a problem called "head-of-line blocking"
HTTP/1.1 tried to fix this with pipelining
Multiplexing addresses these problems by allowing multiple request and response messages to be in flight at the same time; it’s even possible to intermingle parts of one message with another on the wire
Does this reason minimize importance of domain sharding for resource and also bundling resource, spriting images etc? If this is true, should I at least plan for a refactoring? And how does this work?
Multiplexing takes every request and makes them into one request, therefore optimisations surrounding reducing the number of requests are far less useful than in HTTP 1. I would suggest that you plan for refactoring your site/app only if you are migrating your server to HTTP2. Modern browsers are adopting it, however server implementations vary. This was done to ensure that we as developers have the choice to upgrade to HTTP2, in contrast to a 'forced upgrade'.
I read in bundling parts of systemjs documentation that bundling optimizations no longer needed in HTTP/2:
Over HTTP/2 this approach may be preferable as it allows files to be
individually cached in the browser meaning bundle optimizations are no
longer a concern.
My questions:
It means we don't need to think of bundling scripts or other resources when using HTTP/2?
What is in HTTP/2 which makes this feature enable?
The bundling optimization was introduced as a "best practice" when using HTTP/1.1 because browsers could only open a limited number of connections to a particular domain.
A typical web page has 30+ resources to download in order to be rendered.
With HTTP/1.1, a browser opens 6 connections to the server, request 6 resources in parallel, wait for those to be downloaded, then request other 6 resources and so forth (or course some resource will be downloaded faster than others and that connection could be reused before than others for another request).
The point being that with HTTP/1.1 you can only have at most 6 outstanding requests.
To download 30 resources you would need 5 roundtrips, which adds a lot of latency to the page rendering.
In order to make the page rendering faster, with HTTP/1.1 the application developer had to reduce the number of requests for a single page.
This lead to "best practices" such as domain sharding, resource inlining, image spriting, resource bundling, etc., but these are in fact just clever hacks to workaround HTTP/1.1 protocol limitations.
With HTTP/2 things are different because HTTP/2 is multiplexed.
Even without HTTP/2 Push, the multiplexing feature of HTTP/2 renders all those hacks useless, because now you can request hundreds of resources in parallel using a single TCP connection.
With HTTP/2, the same 30 resources would require just 1 roundtrip to be downloaded, giving you a straight 5x performance increase in that operation (that typically dominates the page rendering time).
Given that the trend of web content is to be richer, it will have more resources; the more resources, the better HTTP/2 will perform with respect to HTTP/1.1.
On top of HTTP/2 multiplexing, you have HTTP/2 Push.
Without HTTP/2 Push, the browser has to request the primary resource (the *.html page), download it, parse it, and then arrange to download the 30 resources referenced by the primary resource.
HTTP/2 Push allows you to get the 30 resources while you are requesting the primary resource that references them, saving one more roundtrip, again thanks to the HTTP/2 multiplexing.
It is really the multiplexing feature of HTTP/2 that allows you to forget about resource bundling.
You can look at the slides of the HTTP/2 session that I gave at various conferences.
HTTP/2 supports "server push" which obsoletes bundling of resources. So, yes, if you are you using HTTP/2, bundling would actually be an anti-pattern.
For more info check this: https://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/
Bundling is doing a lot in a modern JavaScript build.
HTTP/2 only addresses the optimisation of minimising the amount of requests between the client and server by making the cost of additional requests much cheaper than with HTTP/1
But bundling today is not only about minimising the count of requests between the client and the server. Two other relevant aspects are:
Tree Shaking: Modern bundlers like WebPack and Rollup can eliminate unused code (even from 3rd party libraries).
Compression: Bigger JavaScript bundles can be better compressed (gzip, zopfli ...)
Also HTTP/2 server push can waste bandwidth by pushing resources that the browser does not need, because he still has them in the cache.
Two good posts about the topic are:
http://engineering.khanacademy.org/posts/js-packaging-http2.htm
https://www.contentful.com/blog/2017/04/04/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling/
Both those posts come to the conclusion that "build processes are here to stay".
Bundling is still useful if your website is
Served on HTTP (HTTP 2.0 requires HTTPS)
Hosted by a server that does not support ALPN and HTTP 2.
Required to support old browsers (Sensitive and Legacy Systems)
Required to support both HTTP 1 and 2 (Graceful Degradation)
There are two HTTP 2.0 features that makes bundling obsolete:
HTTP 2.0 Multiplexing and Concurrency (allows multiple resources to be requested on a single TCP connection)
HTTP 2.0 Server Push (Server push allows the server to preemptively push the responses it thinks the client will need into the client's cache)
PS: Bundling is not the lone optimization technique that would be eliminated by the insurgence of HTTP 2.0 features. Features like image spriting, domain sharding and resource inlining (Image embedding through data URIs) will be affected.
How HTTP 2.0 affects existing web optimization techniques
HTTP 1.1 supports keep alive connections, connections are not closed until "Connection: close" is sent.
So, if the browser, in this case firefox has network.http.pipelining enabled and network.http.pipelining.maxrequests increased isn't the same effect in the end?
I know that these settings are disabled because for some websites this could increase load but I think a simple http header flag could tell the browser that is ok tu use multiplexing and this problem can be solved easier.
Wouldn't be easier to change default settings in browsers than invent a new protocol that increases complexity especially in the http servers?
SPDY has a number of advantages that go beyond what HTTP pipelining can offer, which are described in the SPDY whitepaper:
With pipelining, the server still has to return the responses one at a time in the order they were requested. This can be a problem if the client requests a resource that's dynamically generated before one that is static: the server cannot send any of the "easy" static responses until the dynamically generated one has been generated and sent. With SPDY, responses can be returned out of order or in parallel as they are generated, lowering the total time to receive all resources.
As you noted in your question, not all servers are able to deal with pipelining: it's not just load, some servers actually behave incorrectly when the client requests pipelining. Using a header to indicate that it's okay to do pipelining is too late to get the maximum benefit: you are already receiving the first response at that point, so while you can use it on future connections it's already too late for this one.
SPDY compresses headers using an algorithm which is specific to that task (stateful and with knowledge of what is normally in HTTP headers); while yes, SSL already includes compression, just compressing them with deflate is not as efficient. Most HTTP requests have no bodies and only a short GET line, so the headers make up virtually the entire request: any compression you can get is an improvement. Many responses are also small compared to their headers.
SPDY allows servers to send back additional responses without the client asking for them. For example, a server might start sending back the CSS for a page along with the original HTML, before the client has had a chance to receive and parse the HTML to determine the stylesheet URL. This can speed up page loads even further by eliminating the need for the client to actually parse the HTML before requesting other resources needed to render the page. It also supports a less bandwidth-heavy version of this feature where it can "hint" about which resources might be needed, and allow the client to decide: this allows, for example, clients that don't care about images to not bother to request them, but clients that want to display images can still request the images using the given URLs without needing to wait for the HTML.
Other things too: see William Chan's answer for even more.
HTTP pipelining is susceptible to head of line blocking (http://en.wikipedia.org/wiki/Head-of-line_blocking) at the HTTP transaction level whereas SPDY only has head of line blocking at the transport level, due to its use of multiplexing.
HTTP pipelining has deployability issues. See https://datatracker.ietf.org/doc/html/draft-nottingham-http-pipeline-01 which describes a number of different workarounds and heuristics to mitigate this. SPDY as deployed in the wild does not have this problem since it is generally deployed over SSL (port 443) using NPN (http://technotes.googlecode.com/git/nextprotoneg.html) to negotiate SPDY support. SSL is key, since it prevents intermediaries from interfering.
SPDY has header compression. See http://dev.chromium.org/spdy/spdy-whitepaper which discusses some benchmark results of the benefits of header compression. Now, it's useful to note that bandwidth is less and less of an issue (see http://www.belshe.com/2010/05/24/more-bandwidth-doesnt-matter-much/), but it's also useful to remember that bandwidth is still key for mobile. Check out https://developers.google.com/speed/articles/spdy-for-mobile which shows how beneficial SPDY is for mobile.
SPDY supports features like server push. See http://dev.chromium.org/spdy/spdy-best-practices for ways to use server push to improve cacheability of content and still reduce roundtrips.
HTTP pipelining has ill-defined failure semantics. When the server closes the connection, how do you know which requests have been successfully processed? This is a major reason why POST and other non-idempotent requests are not allowed over pipelined connections. SPDY provides semantics to cancel individual streams on the same connection, and also has a GOAWAY frame which indicates the last stream to be successfully processed.
HTTP pipelining has difficulty, often due to intermediaries, in allowing deep pipelines. This (in addition to many other reasons like HoL blocking) means that you still need to utilize multiple TCP connections to achieve maximal parallelization. Using multiple TCP connections means that congestion control information cannot be shared, that compression contexts cannot be shared (like SPDY does with headers), is worse for the internet (more costly for intermediaries and servers).
I could go on and on about HTTP pipelining vs SPDY. But I'd recommend just reading up on SPDY. Check out http://dev.chromium.org/spdy and our tech talk on SPDY at http://www.youtube.com/watch?v=TNBkxA313kk&list=PLE0E03DF19D90B5F4&index=2&feature=plpp_video.
See Difference between HTTP pipeling and HTTP multiplexing with SPDY
Imagine I'm building an ordinary old website. Not a game, not a chat program, an ordinary website. Let's say it's a stack overflow clone.
The client side would simply make service calls to the server side. The server is essentially a dumb data store and never sends down HTML. The client handles all templating via javascript.
If I established a single websocket connection and did all requests through that, would I see a significant speedup over doing ajax requests?
The obvious advantage to using a single connection is that it only has to be established once. But how much time does that actually save? I know establishing a TCP connection can be costly, but in the grand scheme of things, does it matter?
I would not recommend websockets for webpages. HTTP 1.1 can reuse a TCP-connection for multiple requests, it's only HTTP 1.0 that had to use a new TCP connection for each request.
SPDY is probably a protocol that do what you are looking for. See SPDY: An experimental protocol for a faster web, but it's only supported by Chrome.
If you use websockets, the requests will not be cached.
One HTTP connection can only by used for one HTTP request at the same time. Say that a page requested a 100Kb document, nothing else will be send from the client to the server until that 100Kb document has been transferred. This is called head-of-line blocking. The client can establish an additional connection with the server, but there is also a limit on the amount of concurrent connections with the same server.
One of the primary reasons for developing SPDY and later HTTP/2 was solving this exact problem. However, support for SPDY and HTTP/2 is not yet as widespread as for WebSocket. WebSocket can get you there earlier because it supports multiple streams in full-duplex mode.
Once HTTP/2 is better supported it will be the preferred solution for this problem, but WebSocket will still be better for real-time web applications, where server needs to push data to the client.
Have a look at the N2O framework, it was created to address the problems I described above. In N2O WebSocket is used to send all assets associated with a page.
How much speed you could gain from using WebSocket instead of standard HTTP requests pretty much depends on your specific website: how often it requests data from the server, how big is a typical response, etc.
I've been working with websockets lately in detail. Created my own server and there's a public demo. I don't have such detailed experience or knowledge re: http. (Although since websocket requests are upgraded http requests, I have some.)
On my end, the server reports details of each hit. Among them are a bunch of http keep-alive requests. My server doesn't handle them because they're not websocket requests. But it got my curiosity up.
The whole big thing about websockets is that the connection stays alive. Then you can pass messages in both directions (simultaneously even). I've read that the Keep-Alive HTTP connection is a relatively new development (I don't know how many years in people time, just that it's only included in the latest standard - 1.1 - is that actually old now?)
I guess I can assume that there's a behavioral difference between the two or there would have been no reason for a websocket standard? What's the difference?
A Keep Alive HTTP header since HTTP 1.0, which is used to indicate a HTTP client would like to maintain a persistent connection with HTTP server. The main objects is to eliminate the needs for opening TCP connection for each HTTP request. However, while there is a persistent connection open, the protocol for communication between client and server is still following the basic HTTP request/response pattern. In other word, server side can't push data to client.
WebSocket is completely different mechanism, which is used to setup a persistent, full-duplex connection. With this full-duplex connection, server side can push data to client and client should be expected to process data from server side at any time.
Quoting corresponding entries on Wikipedia for reference:
1) http://en.wikipedia.org/wiki/HTTP_persistent_connection
2) http://en.wikipedia.org/wiki/WebSocket
You should read up on COMET, a design pattern which shows the limits of HTTP Keep-Alive. Keep-Alive is over 12 years old now, so it's not a new feature of HTTP. The problem is that it's not sufficient; the client and server cannot communicate in a truly asynchronous manner. The client must always use a "hanging" request in order to get a message back from the server; the server may not just send a message to the client at any time it wants.
HTTP vs Websockets
REST (HTTP)
Resources benefit from caching when the representation of a resource changes rarely or multiple clients are expected to retrieve the resource.
HTTP methods have well-known idempotency and safety properties. A request is “idempotent” if it can be issued multiple times without resulting in unique outcomes.
The HTTP design allows for responses to describe errors with the request, with the resource, or to provide nuanced status information to differentiate between success scenarios.
Have request and response functionality.
HTTP v1.1 may allow multiple requests to reuse a single connection, there will generally be small timeout periods intended to control resource consumption.
You might be using HTTP incorrectly if…
Your design relies on a client polling the service often, without the user taking action.
Your design requires frequent service calls to send small messages.
The client needs to quickly react to a change to a resource, and it cannot predict when the change will occur.
The resulting design is cost-prohibitive. Ask yourself: Is a WebSocket solution substantially less effort to design, implement, test, and operate?
WebSockets
WebSocket design does not allow explicit or transparent proxies to cache messages, which can degrade client performance.
WebSocket protocol offers support only for error scenarios affecting the establishment of the connection. Once the connection is established and messages are exchanged, any additional error scenarios must be addressed in the messaging layer design, but WebSockets allow for a higher amount of efficiency compared to REST because they do not require the HTTP request/response overhead for each message sent and received.
When a client needs to react quickly to a change (especially one it cannot predict), a WebSocket may be best.
This makes the protocol well suited to “fire and forget” messaging scenarios and poorly suited for transactional requirements.
WebSockets were designed specifically for long-lived connection scenarios, they avoid the overhead of establishing connections and sending HTTP request/response headers, resulting in a significant performance boost
You might be using WebSockets incorrectly if..
The connection is used only for a very small number of events, or a very small amount of time, and the client does not - need to quickly react to the events.
Your feature requires multiple WebSockets to be open to the same service at once.
Your feature opens a WebSocket, sends messages, then closes it—then repeats the process later.
You’re re-implementing a request/response pattern within the messaging layer.
The resulting design is cost-prohibitive. Ask yourself: Is a HTTP solution substantially less effort to design, implement, test, and operate?
Ref: https://blogs.windows.com/buildingapps/2016/03/14/when-to-use-a-http-call-instead-of-a-websocket-or-http-2-0/