├── .gitignore
├── .jshintrc
├── LICENSE
├── README.md
├── examples
├── assets
│ └── justAScript.js
└── crossDomain.html
├── lib
├── logger.js
├── page.js
├── worker.js
└── workerClientsList.js
├── package.json
├── swgetheaders-page.js
└── swgetheaders-worker.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "esversion": 6
3 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Gaël Métais
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Service Worker Get Headers
2 |
3 | Being able to see headers information on every request can be very useful in your browser's network console. But it could sometimes be interesting to access these headers informations from the page, in JavaScript. XMLHttpRequests allow that, but most requests on a page are not XHR.
4 |
5 | Examples of things that it could help you with:
6 | - read file size on images, stylesheets, scripts, fonts, ...
7 | - detect misconfigured server (caching, compression, keep-alive, ...)
8 | - read caching and expiring informations
9 |
10 | *This project is currenlty more a "proof of concept" than a library you can rely on. Please use with extra caution and PLEASE report any bug you see.*
11 |
12 |
13 | ## How it works
14 |
15 | It uses [Service Workers](https://developer.mozilla.org/fr/docs/Web/API/Service_Worker_API) to spy on every request made, read the headers, than send the information back to the page's JavaScript.
16 |
17 | **Your website needs to be on HTTPS.** It works on localhost regardless of protocol.
18 |
19 | For security reasons, many limitations have been built in browsers regarding reading headers on **cross-domain** requests. Have a look at the [Cross domain problems](#cross-domain-problems) chapter.
20 |
21 |
22 | ## Compatible browsers
23 |
24 | This library will only work on browsers that support Service Workers: Chrome, FireFox and Opera. Edge should arrive soon. Have a look on [CanIUse](http://caniuse.com/#feat=serviceworkers).
25 |
26 |
27 | ## Install
28 |
29 | First you need to download the two scripts `swgetheaders-page.js` and `swgetheaders-worker.js` and put them in your workspace.
30 |
31 | #### The page-side script
32 |
33 | The `swgetheaders-page.js` is the library that your code will control. Just call it as a normal script on your page like this:
34 | ```html
35 |
36 | ```
37 | You can (and probably should) concat it with your other scripts.
38 |
39 | #### The worker-side script
40 | The `swgetheaders-worker.js` is the service worker. It needs to be hosted on the same origin as the page.
41 |
42 | Add this line of code on your page to load the worker:
43 | ```js
44 | swgetheaders.registerServiceWorker('/some-path/swgetheaders-worker.js');
45 | ```
46 |
47 | Please note that the worker will not be available on the first page load, but only after a page change or a reload. This is due to some limitations in Service Workers.
48 |
49 |
50 | ## Usage
51 |
52 | You can subscribe to 2 kind of events:
53 |
54 | #### The `plugged` event
55 | ```js
56 | swgetheaders.on('plugged', function() {
57 | console.log('The service worker is now activated and ready to listen to requests');
58 | });
59 | ```
60 | The worker is installed + the communication channel between the page and the worker is open. In most case, this appends lately on the page load and the worker has already spied some network requests. It automatically puts their headers in a waiting list and sends them to the page once the communication channel is open, so you will probably be flooded by previous requests just after this `plugged` event is sent.
61 |
62 | The status of the worker can also be asked at any time like this: `swgetheaders.isPlugged()`
63 |
64 |
65 | #### The `response` event
66 | ```js
67 | swgetheaders.on('response', function(request, response) {
68 | console.log('A response just arrived: ', response);
69 | });
70 | ```
71 |
72 | The worker catched a network response. The callback method takes two arguments:
73 |
74 | - First argument: the `request` object that looks like below:
75 | ```json
76 | {
77 | "method": "GET",
78 | "url": "http://localhost:8080/examples/assets/justAScript.js?q=1465589915112",
79 | "referrer": "http://localhost:8080/examples/checkGzip.html",
80 | "headers": [
81 | {
82 | "name": "accept",
83 | "value": "*/*"
84 | },
85 | {
86 | "name": "user-agent",
87 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2762.0 Safari/537.36"
88 | }
89 | ]
90 | }
91 | ```
92 |
93 | - Second argument: the `response` object that looks like this:
94 | ```json
95 | {
96 | "status": 200,
97 | "statusText": "OK",
98 | "url": "http://localhost:8080/examples/assets/justAScript.js?q=1465589915112",
99 | "headers": [
100 | {
101 | "name": "date",
102 | "value": "Fri, 10 Jun 2016 20:18:35 GMT"
103 | },
104 | {
105 | "name": "last-modified",
106 | "value": "Thu, 09 Jun 2016 16:36:16 GMT"
107 | },
108 | {
109 | "name": "server",
110 | "value": "ecstatic-0.7.6"
111 | },
112 | {
113 | "name": "content-type",
114 | "value": "application/javascript; charset=utf-8"
115 | },
116 | {
117 | "name": "cache-control",
118 | "value": "max-age=3600"
119 | },
120 | {
121 | "name": "connection",
122 | "value": "keep-alive"
123 | },
124 | {
125 | "name": "content-length",
126 | "value": "133"
127 | }
128 | ]
129 | }
130 | ```
131 |
132 |
133 | ## Options
134 |
135 | When calling the `registerServiceWorker` method, you can add some options:
136 |
137 | ```js
138 | var options = {
139 | sameDomainOnly: false,
140 | corsExceptions: [
141 | 'code.jquery.com',
142 | 'platform.twitter.com'
143 | ],
144 | debug: true
145 | };
146 |
147 | swgetheaders.registerServiceWorker('/some-path/swgetheaders-worker.js', options);
148 | ```
149 |
150 | #### `sameOriginOnly`
151 |
152 | When set to `true`, restricts spying to the same-origin requests. All third-party domains will be ignored. Default is `false`.
153 |
154 | #### `corsExceptions`
155 |
156 | By default, the library fetches every cross-domain in `no-cors` mode, to avoid the request to be blocked by the browser's cross-origin limitations. The problem is that the `no-cors` responses are "opaque", which means that headers are not accessible.
157 |
158 | If some of the cross-domain requests on your page respond with the `Access-Control-Allow-Origin` header, use this option to tell the library that it can call them with the `cors` mode. This means that the response headers will be readable (not all of the headers). Have a look at the [Cross domain problems](#cross-domain-problems) chapter for more information.
159 |
160 | This option needs to be an array of strings. When the browser makes a request to an URL, the URL will be checked with a simple `indexOf` on each of the specified exceptions, like this: `if (url.indexOf(exception) >= 0) {cors}`. So you can enter here domain names, file names, full paths, ...
161 |
162 | #### `debug`
163 |
164 | When set to `true`, the library will be much more verbose. Default is `false`.
165 |
166 | ## Full example
167 |
168 | ```js
169 | // Needed because the library uses browserify
170 | var swgetheaders = require('swgetheaders');
171 |
172 | // Register the service worker (with some options)
173 | swgetheaders.registerServiceWorker('/swgetheaders-worker.js', {
174 | debug: true,
175 | corsExceptions: [
176 | 'code.jquery.com',
177 | 'platform.twitter.com'
178 | ]
179 | });
180 |
181 | swgetheaders.on('plugged', function() {
182 | console.log('The service worker is now activated and ready to listen to requests');
183 | });
184 |
185 | swgetheaders.on('response', function(request, response) {
186 | console.log('A response just arrived. We have both the request and the response:', request, response);
187 | });
188 | ```
189 |
190 |
191 | ## Cross domain problems
192 |
193 | CORS restrictions are a bit annoying.
194 |
195 | #### Opaque response
196 | When making a cross-domain request, the service-worker can't access the response headers (neither the response code). The library will fire a `response` event, but the response object will only be: `{"opaque": true}`.
197 |
198 | #### Access-Control-Allow-Origin
199 | If the cross-origin server responds with the `Access-Control-Allow-Origin: *` header (or your domain instead if `*`), then we can have more information than the opaque response. Sadly, it seems that not all headers are available. Just a few of them, such as Expire, Cache-Control, Content-Type, Last-Modified. If you have more information about which headers are concerned, please open an issue or email me.
200 |
201 | To gain access to cross-origin headers, the library needs you to specify an exception with the `corsExceptions` option. Double check the result, because if a request is sent with the exception and the server doesn't respond with the `Access-Control-Allow-Origin` header, then your request fails with an error: `Fetch API cannot load https://platform.twitter.com/widgets.js. No 'Access-Control-Allow-Origin' header is present on the requested resource.`
202 |
203 | If you don't need to spy on cross-origin requests, it's probably safer to use the `sameOriginOnly` option.
204 |
205 | #### Cookies
206 | Service workers disallow sending cookies along with cross-origin requests. This is the same security rules that applies on XMLHttpRequest. It might work if the server responds with a `Access-Control-Allow-Credentials` header, but this library is not compatible with that for the moment.
207 |
208 | Only same-origin cookies are sent.
209 |
210 |
211 | ## What's next?
212 |
213 | This library is still in very early stage. It could be made available as an npm module. It could also be enhanced to allow to modify the headers before sending the request.
214 |
215 | If you want me to go on enhancing the library, please add a star to the project on GitHub. The more stars, the more I'll do!
216 |
217 |
218 | # Author
219 | Gaël Métais. I'm a webperf freelance. Follow me on Twitter [@gaelmetais](https://twitter.com/gaelmetais), I tweet about Web Performances and Front-end!
220 |
221 | I can also help your company implement Service Workers, visit [my website](https://www.gaelmetais.com).
222 |
--------------------------------------------------------------------------------
/examples/assets/justAScript.js:
--------------------------------------------------------------------------------
1 | function thatDoesNothing() {
2 | if (1 === 2) {
3 | window.alert('Your browser has a major problem');
4 | }
5 | }
6 |
7 | thatDoesNothing();
--------------------------------------------------------------------------------
/examples/crossDomain.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
20 |
21 |
Cross domain examples
22 |
The service worker is not fully installed, you need to refresh the page.