Chapter 3: Network requests

3.1 Fetch

JavaScript can send network requests to the server and load new information whenever it’s needed.

AJAX: Asynchronous JavaScript And XML

let promise = fetch(url, [options])
  • url

  • options: method, headers, etc.

  • The promise resolves with an object of the built-in Response class as soon as the server responds with headers. The promise rejects if the fetch was unable to make HTTP-request.

  • status: HTTP status code

  • ok: boolean, true if code is 200-299

  • Response provides multiple promise-based methods to access the body in various.

  • response.text()

  • response.json()

  • response.formData()

  • response.blob()

  • response.arrayBuffer()

  • response.body: ReadableStream object

We can choose only one body-reading method.

Headers

There’s a list of forbidden HTTP headers that we can’t set: Accept-Encoding, Connection, Content-Length, Keep-Alive, etc.

POST requests

  • method: POST

  • body: one of the following:

    • string

    • FormData

    • Blob/BufferSource

    • URLSearchParams

3.2 FormData

If HTML form element is provided, it automatically captures its fields. It has a special type: Content-Type: multipart/form-data.

  • formData.append(name, value): add a form field with the given name and value.

  • formData.append(name, blob, fileName): add a file field

  • formData.delete(name)

  • formData.get(name)

  • formData.has(name)

  • formData.set(name, value): remove other name and add the new one (guaranteed unique)

3.3 Fetch: Download progress

The fetch method allows to track download progress.

response.body gives full control over the reading process, and we can count how much is consumed at any moment.

3.4 Fetch: Abort

AbortController can be used to abort not only fetch, but other asynchronous tasks as well.

  1. Create a controller.

It has a single method abort(), and a single property signal.

When abort() is called:

  • abort event triggers on controller.signal.

  • controller.signal.aborted property becomes true.

  • Pass the signal property to fetch option.

  1. To abort, call controller.abort(). Its promise rejects with an error AbortError.

AbortController is scalable, it allows to cancel multiple fetches at once.

3.5 Fetch: Cross-Origin Requests

If we send a fetch request to another web-site, it will probably fail.

Cross-origin requests require special headers from the remote side.

Simple requests

A simple request is a request that satisfies two conditions: 1. GET, POST or HEAD 2. Headers: Accept, Accept-Language, Content-Language, Content-Type (application/x-www-form-urlencoded, multipart/form-data, or text/plain)

A “simple request” can be made with a <form> or a <script>, without any special methods.

CORS for simple requests

Request

Response

If the Access-Control-Allow-Origin contains the origin or equals *, the response is successful, otherwise an error.

Because Referer is unreliable (can be modified or removed by fetch), Origin was invented, and the correctness of that header is ensured by the browser.

Response headers

For cross-origin request, by default JavaScript may only access simple response headers:

  • Cache-Control

  • Content-Language

  • Content-Type

  • Expires

  • Last-Modified

  • Pragma

To grant JavaScript access to any other response header, the server must send Access-Control-Expose-Headers header.

Non-simple requests

A preflight request uses method OPTIONS, no body and two headers:

  • Access-Control-Request-Method: the method of the non-simple request.

  • Access-Control-Request-Headers: a comma-separated list of its non-simple HTTP-headers.

If the server agrees to serve the requests, then it should respond with empty body, status 200 and headers:

  • Access-Control-Allow-Method

  • Access-Control-Allow-Headers

  • Access-Control-Max-Age: a number of seconds to cache the permissions.

Then the actual request is sent, the previous simple scheme is applied.

Credentials

A cross-origin request by default does not bring any credentials (cookies or HTTP authentication).

Request

Response

Access-Control-Allow-Origin is prohibited from using * for requests with credentials.

3.6 Fetch API

referrer, referrerPolicy

The referrer option allows to set any Referer within the current origin or remove it.

The referrerPolicy option sets general rules for Referer.

  • "no-referrer-when-downgrade": Do not send Referer when we send a request from HTTPS to HTTP.

  • no-referrer

  • origin

  • origin-when-cross-origin: Send origin as Referer when we send a cross-origin request.

  • same-origin: Send full Referer to the same origin, but no Referer for cross-origin requests.

  • strict-origin: Send origin as Referer, but don’t send it for downgrade requests.

  • strict-origin-when-cross-origin: For same-origin send full Referer, for cross-origin send only origin, but don’t send it for downgrade requests.

  • unsafe-url: Always send full url in Referer.

mode

The mode option is a safe-guard that prevents occasional cross-origin requests:

  • cors: the default, cross-origin requests are allowed

  • same-origin: cross-origin requests are forbidden

  • no-cors

credentials

The credentials option specifies whether fetch should send cookies and HTTP-Authorization headers with the request.

  • same-origin: the default, don’t send for cross-origin requests

  • include: always send.

  • omit: never send.

cache

By default, fetch requests make use of standard HTTP-caching. It uses Expires, Cache-Control headers, sends If-Modified-Since, etc.

  • default

  • no-store

  • reload

  • no-cache

  • force-cache

  • only-if-cached

redirect

  • follow

  • error: throw error in case of HTTP-redirect.

  • manual: don’t follow HTTP-redirect, but response.url will be the new URL, and response.redirected will be true.

integrity

The integrity option allows to check if the response matches the known-ahead checksum. (SHA-256, SHA-384, and SHA-512)

keepalive

The keepalive option indicates that the request may outlive the webpage that initiated it.

Normally, when a document is unloaded, all associated network requests are aborted. But keepalive option tells the browser to perform the request in background, even after it leaves the page.

  • The body limit for keepalive requests is 64kb.

  • We can’t handle the response if the document is unloaded.

3.7 URL objects

Creating a URL

  • url: full URL or only path.

  • base: if set and url argument has only path, then the full URL is generated.

  • href: the full url, same as url.toString().

  • protocol: example: https

  • search: a string of parameters, starts with the question mark ?.

  • hash: starts with the hash character #.

SearchParams

Parameters need to be encoded if they contain spaces, non-latin letters, etc.

  • append(name, value)

  • delete(name)

  • get(name)

  • getAll(name)

  • has(name)

  • set(name, value)

  • sort()

Encoding

Encoding strings

  • encodeURI: only characters that are totally forbidden in URL.

  • decodeURI

  • encodeURIComponent: forbidden characters and #, $, &, +, ,, /, :, ;, =, ?, @.

  • decodeURIComponent

3.8 XMLHttpRequest

In modern web-development XMLHttpRequest is used for three reasons:

  • Historical reasons

  • Support old browsers without polyfills

  • Features that are not supported by fetch, such as tracking upload progress

The basics

  1. Create XMLHttpRequest:

  1. Initialize it:

  • method: HTTP-method

  • URL

  • async

  • user, password

  • Send it out:

  1. Listen to xhr events for response.

  2. load: triggers when the request is complete (even if HTTP status is like 400 or 500) and the response is downloaded.

  3. error

  4. progress: triggers periodically while the response is being downloaded, reports how much has been downloaded.

Once the server has responded, xhr has these properties:

  • status

  • statusText: example: OK, NotFound, Forbidden

  • response

We can also specify a timeout using the corresponding property:

xhr.responseType

  • "": string

  • text: string

  • arraybuffer

  • blob

  • document: XML

  • json

xhr.readyState

We can track them using readystatechange event. State 3 repeats every time a data packet is received over the network.

To terminate the request, call xhr.abort().

Synchronous requests

If in the open method the third parameter async is set to false, the request is made synchronously. JavaScript execution pauses at send() and resumes when the response is received.

However, if a synchronous call takes too much time, the browser may suggest to close the “hanging” webpage.

HTTP-headers

Several headers are managed exclusively by the browser, e.g. Referer and Host. It can’t undo setRequestHeader.

POST, FormData

To make a POST request, we can use the built-in FormData object, which is sent with multipart/form-data encoding.

Upload progress

The progress event triggers only on the downloading stage. We can use xhr.upload to track upload events.

It generates these events:

  • loadstart – upload started.

  • progress – triggers periodically during the upload.

  • abort – upload aborted.

  • error – non-HTTP error.

  • load – upload finished successfully.

  • timeout – upload timed out (if timeout property is set).

  • loadend – upload finished with either success or error.

3.9 Resumable file upload

Not-so-useful progress event

The event triggers when the data is sent, instead of receving by the server. Maybe it was buffered by a local network proxy, or maybe the remote server process just died and couldn’t process them.

Algorithm

  1. Create an id to uniquely identify the file.

  2. Send a request to the server, asking how many bytes it already has.

  3. Use Blob method slice to send the file from the break point.

3.10 Long polling

Long polling is the simplest way of having persistent connection with server, that doesn’t use any specific protocol like WebSocket or Server Side Events.

Regular polling

The simplest way to get new information from the server is periodic polling.

  • Messages are passed with a delay up to the period.

  • The server may have too many requests to handle.

Long pooling

Long polling works great in situations when messages are rare.

Every message is a separate request, supplied with headers and authentication overhead.

  1. A request is sent to the server.

  2. The server doesn’t close the connection until it has a message to send.

  3. When a message appears – the server responds to the request with it.

  4. The browser makes a new request immediately.

3.11 WebSocket

The WebSocket protocol provides a way to exchange data between browser and server via a persistent connection.

A simple example

Use wss:// protocol is encrypted and more reliable than ws://.

Events

  • open – connection established,

  • message – data received,

  • error – websocket error,

  • close – connection closed.

Opening a websocket

When new WebSocket(url) is created, it starts connecting immediately.

Here’s an example of browser headers for request:

  • Origin: It allows the server to decide whether or not to talk WebSocket with this website.

  • Connection: Upgrade: The signal to change to protocol.

  • Upgrade: websocket: The requested protocol.

  • Sec-WebSocket-Key: Random security key.

  • Sec-WebSocket-Version: WebSocket protocol version. (Current: 13)

The server should send code 101 response:

Then the connection is made. WebSocket handshake can’t be emulated.

Extensions and subprotocols

The additional headers Sec-WebSocket-Extensions and Sec-WebSocket-Protocol describe extensions and subprotocols.

  • Sec-WebSocket-Extensions: deflate-frame: the browser supports data compression.

  • Sec-WebSocket-Protocol: soap, wamp: we’d like to transfer the data in SOAP or WAMP ("The WebSocket Application Messaging Protocol") protocols.

The server should respond with a list of protocols and extensions that it agrees to use.

Data transfer

WebSocket communication consists of “frames” – data fragments, that can be sent from either side, and can be of several kinds:

  • text frames

  • binary data frames

  • ping/pong frames: used to check the connection (sent by server and responded automatically by browser)

  • connection close frame

WebSocket .send() method can send either text or binary data.

When we receive the data, text always comes as string. And for binary data, we can choose between Blob and ArrayBuffer formats.

Rate limiting

The socket.bufferedAmount property stores how many bytes are buffered at this moment, waiting to be sent over the network.

Connection close

  • code: a special WebSocket closing code.

  • reason: a string that describes the reason of closing.

Most common codes:

  • 1000: the default closure

  • 1006: connection was lost (can't be sent manually)

  • 1001: the party is going away (server shuts down or user leaves the page)

  • 1009: the message is too big to process

  • 1011: unexpected error on server

Connection state

There's the socket.readyState property with values:

  • 0: CONNECTING

  • 1: OPEN

  • 2: CLOSING

  • 3: CLOSED

3.12 Server Sent Events

The Server-Sent Events specification describes a built-in class EventSource, that keeps connection with the server and allows to receive events from it.

It's simpler than WebSocket:

  • Only server sends data

  • Only text

  • Regular HTTP protocol

  • Auto-reconnect

Getting messages

To start receiving messages, we just need to create new EventSource(url).

The server should respond with status 200 and the header Content-Type: text/event-stream.

  • A message text goes after data:, the space after the colon is optional.

  • Messages are delimited with double line breaks \n\n.

  • To send a line break \n, we can immediately send one more data.

Cross-origin requests

EventSource supports cross-origin requests. The remote server will get the Origin header and must respond with Access-Control-Allow-Origin to proceed.

Reconnection

There’s a small delay between reconnections, a few seconds by default. The server can set the recommended delay using retry: in response.

  • If the browser knows that there’s no network connection at the moment, it may wait until the connection appears, and then retry.

  • If the server wants the browser to stop reconnecting, it should respond with HTTP status 204.

    If the browser wants to close the connection, it should call eventSource.close().

Message id

When a connection breaks due to network problems, either side can’t be sure which messages were received. To correctly resume the connection, each message should have an id field.

When a message with id: is received, the browser:

  • Sets the property eventSource.lastEventIdto its value.

  • Sends the header Last-Event-ID with that id when reconnecting.

Connection status: readyState

The EventSource object has readyState property.

  • 0: Connecting

  • 1: Open

  • 2: Closed

Event types

  • message – a message received, available as event.data.

  • open – the connection is open.

  • error – the connection could not be established.

The server may specify another type of event with event: ... at the event start.

To handle custom events, we must use addEventListener:

Last updated