This commit is contained in:
Ilya Kantor 2019-08-10 16:32:43 +03:00
parent b2bbd1c781
commit 69acca8637
8 changed files with 63 additions and 60 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Before After
Before After

View file

@ -118,7 +118,7 @@ That's all. Now they can interact without limitations. Again, that's only possib
## Iframe: wrong document pitfall ## Iframe: wrong document pitfall
When an iframe comes from the same origin, and we may access its `document`, there's a pitfall. It's not related to cross-domain things, but important to know. When an iframe comes from the same origin, and we may access its `document`, there's a pitfall. It's not related to cross-origin things, but important to know.
Upon its creation an iframe immediately has a document. But that document is different from the one that loads into it! Upon its creation an iframe immediately has a document. But that document is different from the one that loads into it!

View file

@ -91,7 +91,7 @@ Let's explain that step-by-step:
Please note, we can't use both these methods to read the same response: either use a reader or a response method to get the result. Please note, we can't use both these methods to read the same response: either use a reader or a response method to get the result.
2. Prior to reading, we can figure out the full response length from the `Content-Length` header. 2. Prior to reading, we can figure out the full response length from the `Content-Length` header.
It may be absent for cross-domain requests (see chapter <info:fetch-crossorigin>) and, well, technically a server doesn't have to set it. But usually it's at place. It may be absent for cross-origin requests (see chapter <info:fetch-crossorigin>) and, well, technically a server doesn't have to set it. But usually it's at place.
3. Call `await reader.read()` until it's done. 3. Call `await reader.read()` until it's done.
We gather response chunks in the array `chunks`. That's important, because after the response is consumed, we won't be able to "re-read" it using `response.json()` or another way (you can try, there'll be an error). We gather response chunks in the array `chunks`. That's important, because after the response is consumed, we won't be able to "re-read" it using `response.json()` or another way (you can try, there'll be an error).

View file

@ -1,10 +1,6 @@
# Fetch: Cross-Origin Requests # Fetch: Cross-Origin Requests
If we make a `fetch` from an arbitrary web-site, that will probably fail. If we send a `fetch` request to another web-site, it will probably fail.
The core concept here is *origin* -- a domain/port/protocol triplet.
Cross-origin requests -- those sent to another domain (even a subdomain) or protocol or port -- require special headers from the remote side. That policy is called "CORS": Cross-Origin Resource Sharing.
For instance, let's try fetching `http://example.com`: For instance, let's try fetching `http://example.com`:
@ -18,19 +14,25 @@ try {
Fetch fails, as expected. Fetch fails, as expected.
## Why? A brief history The core concept here is *origin* -- a domain/port/protocol triplet.
Because cross-origin restrictions protect the internet from evil hackers. Cross-origin requests -- those sent to another domain (even a subdomain) or protocol or port -- require special headers from the remote side.
That policy is called "CORS": Cross-Origin Resource Sharing.
## Why CORS is needed? A brief history
CORS exists protect the internet from evil hackers.
Seriously. Let's make a very brief historical digression. Seriously. Let's make a very brief historical digression.
**For many years a script from one site could not access the content of another site.** **For many years a script from one site could not access the content of another site.**
That simple, yet powerful rule was a foundation of the internet security. E.g. a script from the page `hacker.com` could not access user's mailbox at `gmail.com`. People felt safe. That simple, yet powerful rule was a foundation of the internet security. E.g. an evil script from website `hacker.com` could not access user's mailbox at website `gmail.com`. People felt safe.
JavaScript also did not have any special methods to perform network requests at that time. It was a toy language to decorate a web page. JavaScript also did not have any special methods to perform network requests at that time. It was a toy language to decorate a web page.
But web developers demanded more power. A variety of tricks were invented to work around the limitation. But web developers demanded more power. A variety of tricks were invented to work around the limitation and make requests to other websites.
### Using forms ### Using forms
@ -50,34 +52,36 @@ One way to communicate with another server was to submit a `<form>` there. Peopl
</form> </form>
``` ```
So, it was possible to make a GET/POST request to another site, even without networking methods. But as it's forbidden to access the content of an `<iframe>` from another site, it wasn't possible to read the response. So, it was possible to make a GET/POST request to another site, even without networking methods, as forms can send data anywhere. But as it's forbidden to access the content of an `<iframe>` from another site, it wasn't possible to read the response.
As we can see, forms allowed to send data anywhere, but not receive the response. To be precise, there wre actually tricks for that (required special scripts at both the iframe and the page), but let these dinosaurs rest in peace. To be precise, there were actually tricks for that, they required special scripts at both the iframe and the page. So the communication with the iframe was technically possible. Right now there's no point to go into details, let these dinosaurs rest in peace.
### Using scripts ### Using scripts
Another trick was to use a `<script src="http://another.com/…">` tag. A script could have any `src`, from any domain. But again -- it was impossible to access the raw content of such script. Another trick was to use a `script` tag. A script could have any `src`, with any domain, like `<script src="http://another.com/…">`. It's possible to execute a script from any website.
If `another.com` intended to expose data for this kind of access, then a so-called "JSONP (JSON with padding)" protocol was used. If a website, e.g. `another.com` intended to expose data for this kind of access, then a so-called "JSONP (JSON with padding)" protocol was used.
Let's say we need to get the data from `http://another.com` this way: Here's how it worked.
Let's say we, at our site, need to get the data from `http://another.com`, such as the weather:
1. First, in advance, we declare a global function to accept the data, e.g. `gotWeather`. 1. First, in advance, we declare a global function to accept the data, e.g. `gotWeather`.
```js ```js
// 1. Declare the function to process the data // 1. Declare the function to process the weather data
function gotWeather({ temperature, humidity }) { function gotWeather({ temperature, humidity }) {
alert(`temperature: ${temperature}, humidity: ${humidity}`); alert(`temperature: ${temperature}, humidity: ${humidity}`);
} }
``` ```
2. Then we make a `<script>` tag with `src="http://another.com/weather.json?callback=gotWeather"`, please note that the name of our function is its `callback` parameter. 2. Then we make a `<script>` tag with `src="http://another.com/weather.json?callback=gotWeather"`, using the name of our function as the `callback` URL-parameter.
```js ```js
let script = document.createElement('script'); let script = document.createElement('script');
script.src = `http://another.com/weather.json?callback=gotWeather`; script.src = `http://another.com/weather.json?callback=gotWeather`;
document.body.append(script); document.body.append(script);
``` ```
3. The remote server dynamically generates a script that calls `gotWeather(...)` with the data it wants us to receive. 3. The remote server `another.com` dynamically generates a script that calls `gotWeather(...)` with the data it wants us to receive.
```js ```js
// The expected answer from the server looks like this: // The expected answer from the server looks like this:
gotWeather({ gotWeather({
@ -87,17 +91,16 @@ Let's say we need to get the data from `http://another.com` this way:
``` ```
4. When the remote script loads and executes, `gotWeather` runs, and, as it's our function, we have the data. 4. When the remote script loads and executes, `gotWeather` runs, and, as it's our function, we have the data.
That works, and doesn't violate security, because both sides agreed to pass the data this way. And, when both sides agree, it's definitely not a hack. There are still services that provide such access, as it works even for very old browsers. That works, and doesn't violate security, because both sides agreed to pass the data this way. And, when both sides agree, it's definitely not a hack. There are still services that provide such access, as it works even for very old browsers.
After a while, networking methods appeared, such as `XMLHttpRequest`. After a while, networking methods appeared in browser JavaScript.
At first, cross-origin requests were forbidden. But as a result of long discussions, cross-domain requests were allowed, in a way that does not add any capabilities unless explicitly allowed by the server. At first, cross-origin requests were forbidden. But as a result of long discussions, cross-origin requests were allowed, but any new capabilities unless require an explicit allowance by the server, expressed in special headers.
## Simple requests ## Simple requests
There are two types of cross-domain requests: There are two types of cross-origin requests:
1. Simple requests. 1. Simple requests.
2. All the others. 2. All the others.
@ -124,7 +127,7 @@ When we try to make a non-simple request, the browser sends a special "preflight
And, unless the server explicitly confirms that with headers, a non-simple request is not sent. And, unless the server explicitly confirms that with headers, a non-simple request is not sent.
Now we'll go into details. All of them serve a single purpose -- to ensure that new cross-origin capabilities are only accessible with an explicit permission from the server. Now we'll go into details.
## CORS for simple requests ## CORS for simple requests
@ -141,13 +144,13 @@ Origin: https://javascript.info
... ...
``` ```
As you can see, `Origin` contains exactly the origin (domain/protocol/port), without a path. As you can see, `Origin` header contains exactly the origin (domain/protocol/port), without a path.
The server can inspect the `Origin` and, if it agrees to accept such a request, adds a special header `Access-Control-Allow-Origin` to the response. That header should contain the allowed origin (in our case `https://javascript.info`), or a star `*`. Then the response is successful, otherwise an error. The server can inspect the `Origin` and, if it agrees to accept such a request, adds a special header `Access-Control-Allow-Origin` to the response. That header should contain the allowed origin (in our case `https://javascript.info`), or a star `*`. Then the response is successful, otherwise an error.
The browser plays the role of a trusted mediator here: The browser plays the role of a trusted mediator here:
1. It ensures that the corrent `Origin` is sent with a cross-domain request. 1. It ensures that the corrent `Origin` is sent with a cross-origin request.
2. If checks for correct `Access-Control-Allow-Origin` in the response, if it is so, then JavaScript access, otherwise forbids with an error. 2. If checks for permitting `Access-Control-Allow-Origin` in the response, if it exists, then JavaScript is allowed to access the response, otherwise it fails with an error.
![](xhr-another-domain.svg) ![](xhr-another-domain.svg)
@ -217,7 +220,7 @@ If the server agrees to serve the requests, then it should respond with status 2
![](xhr-preflight.svg) ![](xhr-preflight.svg)
Let's see how it works step-by-step on example, for a cross-domain `PATCH` request (this method is often used to update data): Let's see how it works step-by-step on example, for a cross-origin `PATCH` request (this method is often used to update data):
```js ```js
let response = await fetch('https://site.com/service.json', { let response = await fetch('https://site.com/service.json', {
@ -303,7 +306,7 @@ Now everything's correct. JavaScript is able to read the full response.
A cross-origin request by default does not bring any credentials (cookies or HTTP authentication). A cross-origin request by default does not bring any credentials (cookies or HTTP authentication).
That's uncommon for HTTP-requests. Usually, a request to `http://site.com` is accompanied by all cookies from that domain. But cross-domain requests made by JavaScript methods are an exception. That's uncommon for HTTP-requests. Usually, a request to `http://site.com` is accompanied by all cookies from that domain. But cross-origin requests made by JavaScript methods are an exception.
For example, `fetch('http://another.com')` does not send any cookies, even those that belong to `another.com` domain. For example, `fetch('http://another.com')` does not send any cookies, even those that belong to `another.com` domain.

View file

@ -1,51 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg width="610px" height="411px" viewBox="0 0 610 411" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="621px" height="411px" viewBox="0 0 621 411" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com --> <!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
<title>xhr-another-domain.svg</title> <title>xhr-another-domain.svg</title>
<desc>Created with sketchtool.</desc> <desc>Created with sketchtool.</desc>
<g id="network" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="network" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="xhr-another-domain.svg"> <g id="xhr-another-domain.svg">
<rect id="Rectangle-227" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="2" y="16" width="128" height="64"></rect> <rect id="Rectangle-227" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="1" y="16" width="128" height="64"></rect>
<text id="JavaScript" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" fill="#8A704D"> <text id="JavaScript" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="36" y="50">JavaScript</tspan> <tspan x="35" y="50">JavaScript</tspan>
</text> </text>
<rect id="Rectangle-228" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="238" y="16" width="128" height="64"></rect> <rect id="Rectangle-228" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="249" y="16" width="128" height="64"></rect>
<text id="Browser" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" fill="#8A704D"> <text id="Browser" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="273" y="50">Browser</tspan> <tspan x="284" y="50">Browser</tspan>
</text> </text>
<rect id="Rectangle-229" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="481" y="16" width="128" height="64"></rect> <rect id="Rectangle-229" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="492" y="16" width="128" height="64"></rect>
<text id="Server" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" fill="#8A704D"> <text id="Server" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="521" y="50">Server</tspan> <tspan x="532" y="50">Server</tspan>
</text> </text>
<path d="M67,81 L67,401" id="Line" stroke="#979797" stroke-linecap="square"></path> <path d="M66,81 L66,401" id="Line" stroke="#979797" stroke-linecap="square"></path>
<path d="M303,81 L303,401" id="Line" stroke="#979797" stroke-linecap="square"></path> <path d="M314,81 L314,401" id="Line" stroke="#979797" stroke-linecap="square"></path>
<path d="M546,81 L546,401" id="Line" stroke="#979797" stroke-linecap="square"></path> <path d="M557,81 L557,401" id="Line" stroke="#979797" stroke-linecap="square"></path>
<path id="Line" d="M284,141 L67,141 L67,139 L284,139 L284,133 L298,140 L284,147 L284,141 Z" fill="#EE6B47" fill-rule="nonzero"></path> <path id="Line" d="M295,141 L67,141 L67,139 L295,139 L295,133 L309,140 L295,147 L295,141 Z" fill="#EE6B47" fill-rule="nonzero"></path>
<text id="Origin:-https://java" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="26" fill="#8A704D"> <text id="Origin:-https://java" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="26" fill="#8A704D">
<tspan x="331.3125" y="206">Origin: https://javascript.info</tspan> <tspan x="342.3125" y="205">Origin: https://javascript.info</tspan>
</text> </text>
<text id="HTTP-request" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="26" fill="#8A704D"> <text id="HTTP-request" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="26" fill="#8A704D">
<tspan x="378.015137" y="169">HTTP-request</tspan> <tspan x="389.015137" y="174">HTTP-request</tspan>
</text> </text>
<text id="fetch()" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" fill="#8A704D"> <text id="fetch()" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="154" y="128">fetch()</tspan> <tspan x="159" y="131">fetch()</tspan>
</text> </text>
<text id="HTTP-response" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="27" fill="#8A704D"> <text id="HTTP-response" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="27" fill="#8A704D">
<tspan x="374.918945" y="250">HTTP-response</tspan> <tspan x="386.418945" y="250">HTTP-response</tspan>
</text> </text>
<text id="Access-Control-Allow" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="18" fill="#8A704D"> <text id="Access-Control-Allow" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="18" fill="#8A704D">
<tspan x="332" y="288">Access-Control-Allow-Origin: </tspan> <tspan x="343" y="287">Access-Control-Allow-Origin: </tspan>
<tspan x="337.130371" y="306">* OR https://javascript.info</tspan> <tspan x="348.130371" y="305">* OR https://javascript.info</tspan>
</text> </text>
<text id="if-the-header-allows" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="18" fill="#8A704D"> <text id="if-the-header-allows" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="18" fill="#8A704D">
<tspan x="92" y="322">if the header allows, success,</tspan> <tspan x="97" y="324">if the header allows, success,</tspan>
</text> </text>
<text id="otherwise-fail" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="18" fill="#8A704D"> <text id="otherwise-fail" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" line-spacing="18" fill="#8A704D">
<tspan x="142" y="356">otherwise fail</tspan> <tspan x="147" y="353">otherwise fail</tspan>
</text> </text>
<path id="Line" d="M531,188 L305,188 L305,186 L531,186 L531,180 L545,187 L531,194 L531,188 Z" fill="#EE6B47" fill-rule="nonzero"></path> <path id="Line" d="M542,188 L316,188 L316,186 L542,186 L542,180 L556,187 L542,194 L542,188 Z" fill="#EE6B47" fill-rule="nonzero"></path>
<path id="Line-2" d="M317,264 L545,264 L545,266 L317,266 L317,272 L303,265 L317,258 L317,264 Z" fill="#EE6B47" fill-rule="nonzero"></path> <path id="Line-2" d="M328,264 L556,264 L556,266 L328,266 L328,272 L314,265 L328,258 L328,264 Z" fill="#EE6B47" fill-rule="nonzero"></path>
<path id="Line-3" d="M85,332 L302,332 L302,334 L85,334 L85,340 L71,333 L85,326 L85,332 Z" fill="#EE6B47" fill-rule="nonzero"></path> <path id="Line-3" d="M85,332 L313,332 L313,334 L85,334 L85,340 L71,333 L85,326 L85,332 Z" fill="#EE6B47" fill-rule="nonzero"></path>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

View file

@ -459,7 +459,7 @@ function upload(file) {
## Cross-origin requests ## Cross-origin requests
`XMLHttpRequest` can make cross-domain requests, using the same CORS policy as [fetch](info:fetch-crossorigin). `XMLHttpRequest` can make cross-origin requests, using the same CORS policy as [fetch](info:fetch-crossorigin).
Just like `fetch`, it doesn't send cookies and HTTP-authorization to another origin by default. To enable them, set `xhr.withCredentials` to `true`: Just like `fetch`, it doesn't send cookies and HTTP-authorization to another origin by default. To enable them, set `xhr.withCredentials` to `true`:

View file

@ -64,7 +64,7 @@ eventSource.onmessage = function(event) {
// or eventSource.addEventListener('message', ...) // or eventSource.addEventListener('message', ...)
``` ```
### Cross-domain requests ### Cross-origin requests
`EventSource` supports cross-origin requests, like `fetch` any other networking methods. We can use any URL: `EventSource` supports cross-origin requests, like `fetch` any other networking methods. We can use any URL:
@ -82,7 +82,7 @@ let source = new EventSource("https://another-site.com/events", {
}); });
``` ```
Please see the chapter <info:fetch-crossorigin> for more details about cross-domain headers. Please see the chapter <info:fetch-crossorigin> for more details about cross-origin headers.
## Reconnection ## Reconnection
@ -227,9 +227,9 @@ The syntax is:
let source = new EventSource(url, [credentials]); let source = new EventSource(url, [credentials]);
``` ```
The second argument has only one possible option: `{ withCredentials: true }`, it allows sending cross-domain credentials. The second argument has only one possible option: `{ withCredentials: true }`, it allows sending cross-origin credentials.
Overall cross-domain security is same as for `fetch` and other network methods. Overall cross-origin security is same as for `fetch` and other network methods.
### Properties of an `EventSource` object ### Properties of an `EventSource` object

Binary file not shown.