├── LICENSE ├── README.md └── chrome ├── headersBackground.html ├── headersBackground.js ├── headersHelpers.js ├── headersLive.html ├── headersLive.js ├── headersOptions.html ├── headersOptions.js ├── headersPopup.html ├── headersPopup.js ├── icon-128.png ├── icon-16.png ├── icon-256.png ├── icon-32.png ├── icon-48.png ├── icon-64.png └── manifest.json /LICENSE: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | 3 | HTTP Headers - https://www.paulhempshall.com/io/http-headers/ 4 | Copyright (C) 2016-2021, Paul Hempshall. All rights reserved. 5 | 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | 10 | No redistribution without prior written consent. 11 | 12 | --------------------------------------------------------------------- 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTTP Headers ![Supported](https://img.shields.io/badge/browser-chrome-green.svg "Supported Platforms") 2 | 3 | A web browser extension that can display the current tab's HTTP request and response or a live stream of HTTP requests and responses. 4 | 5 | Features: 6 | 7 | - Light and Dark Theme 8 | - Per tab HTTP Headers 9 | - Live HTTP Headers tab 10 | - A bunch of filters 11 | 12 | Currently available for Chrome 13 | 14 | ## Installation 15 | 16 | Install directly from the Chrome webstore: 17 | https://chrome.google.com/webstore/detail/http-headers/fabjnpecogealbfoebkcjfbmdhnnfhbj 18 | 19 | OR 20 | 21 | Download the latest package for your browser from the [releases](https://github.com/phempshall/http-headers/releases) page. 22 | 23 | ### Chrome Installation 24 | 25 | 1. Download the **chrome.crx** package from the [releases](https://github.com/phempshall/http-headers/releases) page 26 | 2. Navigate to the downloaded file on your local computer 27 | 3. Open Chrome > Settings > Extensions 28 | 4. Drag and drop **chrome.crx** file from computer on to the open Chrome Extensions settings page 29 | 5. Click accept to install the addon (displayed as a warning at the top of the browser window) 30 | 31 | 32 | 33 | ## License 34 | 35 | HTTP Headers - https://www.paulhempshall.com/io/http-headers/ 36 | Copyright (C) 2016-2021, Paul Hempshall. All rights reserved. 37 | 38 | This program is distributed in the hope that it will be useful, 39 | but WITHOUT ANY WARRANTY; without even the implied warranty of 40 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 41 | 42 | No redistribution without prior written consent. 43 | 44 | --- 45 | [![Bitcoin](https://img.shields.io/badge/donations-Bitcoin-orange.svg)](https://blockchain.info/address/1K1AhrU5JS8euypB3Vw2iGxXqsbwcf9kxN) [![Litecoin](https://img.shields.io/badge/donations-Litecoin-lightgrey.svg)](http://ltc.blockr.io/address/info/LLowTnsW4d3uymbZiiFZLUkejZCcdcmW6F) [![Dogecoin](https://img.shields.io/badge/donations-Dogecoin-yellow.svg)](https://dogechain.info/address/DGB5acV5rfEZaovAM1PNHmbbecrrwb1jsG) [![Dash](https://img.shields.io/badge/donations-Dash-blue.svg)](https://explorer.dash.org/address/XpRyt7DGjprwZxV5Bqh9y2WmBzWaKPmqX5) [![Ethereum](https://img.shields.io/badge/donations-Ethereum-93a1c6.svg)](https://etherscan.io/address/0xe8b4f8842bf14b9a4ce675461153ea21ca742bc7) 46 | -------------------------------------------------------------------------------- /chrome/headersBackground.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | HTTP Headers 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /chrome/headersBackground.js: -------------------------------------------------------------------------------- 1 | /** 2 | HTTP Headers - https://www.paulhempshall.com/io/http-headers/ 3 | Copyright (C) 2016-2021, Paul Hempshall. All rights reserved. 4 | 5 | This program is distributed in the hope that it will be useful, 6 | but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 8 | 9 | No redistribution without prior written consent. 10 | */ 11 | 12 | 'use strict'; 13 | 14 | var defaultSettings = { 15 | 'o_theme': 'o_theme_light', 16 | 'o_live_output': 'o_live_output_formatted', 17 | 'o_live_direction': ['o_live_direction_in', 'o_live_direction_out'], 18 | 'o_live_type': ['o_live_type_main_frame', 'o_live_type_sub_frame', 'o_live_type_stylesheet', 'o_live_type_script', 'o_live_type_image', 'o_live_type_object', 'o_live_type_xmlhttprequest', 'o_live_type_other'], 19 | 'o_live_donation': 'o_live_donation_show', 20 | }; 21 | var currentSettings; 22 | 23 | var headers = {}; 24 | 25 | var filters = { 26 | urls: [""], 27 | types: ["main_frame"] 28 | }; 29 | 30 | /* headers sent */ 31 | chrome.webRequest.onSendHeaders.addListener(function(details) { 32 | headers[details.tabId] = headers[details.tabId] || {}; 33 | headers[details.tabId].request = details; 34 | }, filters, ["requestHeaders"]); 35 | 36 | /* headers received */ 37 | chrome.webRequest.onHeadersReceived.addListener(function(details) { 38 | headers[details.tabId] = headers[details.tabId] || {}; 39 | headers[details.tabId].response = details; 40 | }, filters, ["responseHeaders"]); 41 | 42 | /* remove tab data from headers object when tab is onRemoved */ 43 | chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) { 44 | delete headers[tabId]; 45 | }); 46 | 47 | function get_options() { 48 | chrome.storage.sync.get( 49 | defaultSettings, 50 | function (settings) { 51 | currentSettings = settings; 52 | } 53 | ); 54 | } 55 | get_options(); 56 | -------------------------------------------------------------------------------- /chrome/headersHelpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | HTTP Headers - https://www.paulhempshall.com/io/http-headers/ 3 | Copyright (C) 2016-2021, Paul Hempshall. All rights reserved. 4 | 5 | This program is distributed in the hope that it will be useful, 6 | but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 8 | 9 | No redistribution without prior written consent. 10 | */ 11 | 12 | 'use strict'; 13 | 14 | const httpStatusCodes = { 15 | //1×× Informational 16 | '100' : 'Continue', 17 | '101' : 'Switching Protocols', 18 | '102' : 'Processing', 19 | 20 | //2×× Success 21 | '200' : 'OK', 22 | '201' : 'Created', 23 | '202' : 'Accepted', 24 | '203' : 'Non-authoritative Information', 25 | '204' : 'No Content', 26 | '205' : 'Reset Content', 27 | '206' : 'Partial Content', 28 | '207' : 'Multi-Status', 29 | '208' : 'Already Reported', 30 | '226' : 'IM Used', 31 | 32 | //3×× Redirection 33 | '300' : 'Multiple Choices', 34 | '301' : 'Moved Permanently', 35 | '302' : 'Found', 36 | '303' : 'See Other', 37 | '304' : 'Not Modified', 38 | '305' : 'Use Proxy', 39 | '307' : 'Temporary Redirect', 40 | '308' : 'Permanent Redirect', 41 | 42 | //4×× Client Error 43 | '400' : 'Bad Request', 44 | '401' : 'Unauthorized', 45 | '402' : 'Payment Required', 46 | '403' : 'Forbidden', 47 | '404' : 'Not Found', 48 | '405' : 'Method Not Allowed', 49 | '406' : 'Not Acceptable', 50 | '407' : 'Proxy Authentication Required', 51 | '408' : 'Request Timeout', 52 | '409' : 'Conflict', 53 | '410' : 'Gone', 54 | '411' : 'Length Required', 55 | '412' : 'Precondition Failed', 56 | '413' : 'Payload Too Large', 57 | '414' : 'Request-URI Too Long', 58 | '415' : 'Unsupported Media Type', 59 | '416' : 'Requested Range Not Satisfiable', 60 | '417' : 'Expectation Failed', 61 | '418' : 'I\'m a teapot', 62 | '421' : 'Misdirected Request', 63 | '422' : 'Unprocessable Entity', 64 | '423' : 'Locked', 65 | '424' : 'Failed Dependency', 66 | '426' : 'Upgrade Required', 67 | '428' : 'Precondition Required', 68 | '429' : 'Too Many Requests', 69 | '431' : 'Request Header Fields Too Large', 70 | '444' : 'Connection Closed Without Response', 71 | '451' : 'Unavailable For Legal Reasons', 72 | '499' : 'Client Closed Request', 73 | 74 | //5×× Server Error 75 | '500' : 'Internal Server Error', 76 | '501' : 'Not Implemented', 77 | '502' : 'Bad Gateway', 78 | '503' : 'Service Unavailable', 79 | '504' : 'Gateway Timeout', 80 | '505' : 'HTTP Version Not Supported', 81 | '506' : 'Variant Also Negotiates', 82 | '507' : 'Insufficient Storage', 83 | '508' : 'Loop Detected', 84 | '510' : 'Not Extended', 85 | '511' : 'Network Authentication Required', 86 | '599' : 'Network Connect Timeout Error', 87 | }; 88 | -------------------------------------------------------------------------------- /chrome/headersLive.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | HTTP Headers 15 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 371 | 372 |
373 |
374 |
375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 |
#TimeMethodStatusURL
390 |
391 | 392 |
393 |
394 | Request/Response 395 |
396 |
397 | 398 | 399 |
400 |
401 |

Additional Filters

402 | 403 |
404 |

Filter by direction:

405 | 406 | 407 |
408 | 409 |
410 |

Filter by type:

411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 |
420 | 421 |
422 |

Filter by method:

423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 |
433 | 434 |
435 |

Filter by status:

436 | 439 |
440 | 441 |
442 |

443 | 444 |

445 |
446 |
447 |
448 |
449 |
450 | 451 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | -------------------------------------------------------------------------------- /chrome/headersLive.js: -------------------------------------------------------------------------------- 1 | /** 2 | HTTP Headers - https://www.paulhempshall.com/io/http-headers/ 3 | Copyright (C) 2016-2021, Paul Hempshall. All rights reserved. 4 | 5 | This program is distributed in the hope that it will be useful, 6 | but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 8 | 9 | No redistribution without prior written consent. 10 | */ 11 | 12 | 'use strict'; 13 | 14 | const DOM = { 15 | 'toggleButton' : document.getElementById('toggleButton'), 16 | 'resetButton' : document.getElementById('resetButton'), 17 | 'goToOptionsButton' : document.getElementById('goToOptionsButton'), 18 | 'streamData' : document.getElementById('streamData'), 19 | 'dataArea' : document.getElementById('dataArea'), 20 | 'dataFormattedButton' : document.getElementById('o_live_output_formatted'), 21 | 'dataRawButton' : document.getElementById('o_live_output_raw'), 22 | 'filterByTabs' : document.getElementById('filterByTabs'), 23 | 'filterByDirection' : document.getElementsByName('filterByDirection'), 24 | 'filterByType' : document.getElementsByName('filterByType'), 25 | 'filterByMethod' : document.getElementsByName('filterByMethod'), 26 | 'filterByStatus' : document.getElementById('filterByStatus'), 27 | 'filterByTabs_styleElement' : document.getElementById('filterByTabs_styleElement'), 28 | 'filterByDirection_styleElement' : document.getElementById('filterByDirection_styleElement'), 29 | 'filterByType_styleElement' : document.getElementById('filterByType_styleElement'), 30 | 'filterByMethod_styleElement' : document.getElementById('filterByMethod_styleElement'), 31 | 'filterByStatus_styleElement' : document.getElementById('filterByStatus_styleElement'), 32 | 'clearFiltersButton' : document.getElementById('clearFiltersButton'), 33 | 'donations' : document.getElementById('donations'), 34 | }; 35 | 36 | var p = false; // pause 37 | var n = 0; // number 38 | var a = null; // active row 39 | var s = true; // scroll 40 | var t = document.getElementById(chrome.extension.getBackgroundPage().currentSettings.o_live_output); // default output type (formatted/raw) 41 | 42 | function getStatusCodeStyle (statusCode) { 43 | statusCode = statusCode.toString(); 44 | if (statusCode.match(/^1/)) { return 'status-100' } 45 | if (statusCode.match(/^2/)) { return 'status-200' } 46 | if (statusCode.match(/^3/)) { return 'status-300' } 47 | if (statusCode.match(/^4/)) { return 'status-400' } 48 | if (statusCode.match(/^5/)) { return 'status-500' } 49 | return!1; 50 | } 51 | 52 | var generate_dom = { 53 | 'shouldPrint' : function (details) { 54 | if (p === true) { return false; } 55 | 56 | let direction = (details.responseHeaders ? "in" : "out"); 57 | for (let i = 0; i < chrome.extension.getBackgroundPage().currentSettings.o_live_direction.length; i++) { 58 | if (direction === chrome.extension.getBackgroundPage().currentSettings.o_live_direction[i].replace(/o_live_direction_/,'')) { 59 | for (let i = 0; i < chrome.extension.getBackgroundPage().currentSettings.o_live_type.length; i++) { 60 | if (details.type === chrome.extension.getBackgroundPage().currentSettings.o_live_type[i].replace(/o_live_type_/,'')) { return true } 61 | } 62 | } 63 | } 64 | 65 | return false; 66 | }, 67 | 'filterByTabs' : function (tab, type) { 68 | if (type === 'new') { 69 | DOM.filterByTabs.innerHTML += ''; 70 | } 71 | else if (type === 'update') { 72 | for (let i = 0; i < DOM.filterByTabs.length; i++) { 73 | if (DOM.filterByTabs.options[i].value == tab.id) { 74 | DOM.filterByTabs.options[i].innerHTML = tab.title; 75 | } 76 | } 77 | } 78 | else if (type === 'remove') { 79 | for (let i = 0; i < DOM.filterByTabs.length; i++) { 80 | if (DOM.filterByTabs.options[i].value == tab) { 81 | DOM.filterByTabs.remove(i); 82 | } 83 | } 84 | } 85 | }, 86 | 'filterByStatus' : function () { 87 | for (status in httpStatusCodes) { 88 | DOM.filterByStatus.innerHTML += ''; 89 | } 90 | }, 91 | 'information' : function (row, type = 'o_live_output_raw') { 92 | if (type === 'o_live_output_formatted') { 93 | let details = JSON.parse(row.dataset.details); 94 | DOM.dataArea.innerHTML = ''; 95 | 96 | if (details.requestHeaders) { 97 | DOM.dataArea.innerHTML += "#REQUEST\n"; 98 | } 99 | else if (details.responseHeaders) { 100 | DOM.dataArea.innerHTML += "#RESPONSE\n"; 101 | } 102 | 103 | DOM.dataArea.innerHTML += "Request ID: " + details.requestId + "\n"; 104 | DOM.dataArea.innerHTML += "Type: " + details.type + "\n"; 105 | DOM.dataArea.innerHTML += "Time: " + Date(details.timeStamp) + "\n"; 106 | 107 | if (details.requestHeaders) { 108 | DOM.dataArea.innerHTML += "Method: " + details.method + " " + decodeURIComponent(details.url) + "\n\n"; 109 | details.requestHeaders.sort(function(a,b) {return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);}); 110 | for (let i = 0; i < details.requestHeaders.length; i++) { 111 | DOM.dataArea.innerHTML += details.requestHeaders[i].name + ": " + details.requestHeaders[i].value + "\n"; 112 | } 113 | } 114 | else if (details.responseHeaders) { 115 | DOM.dataArea.innerHTML += "Method: " + details.method + " " + decodeURIComponent(details.url) + "\n"; 116 | DOM.dataArea.innerHTML += "Status: " + row.dataset.statusCode + " - " + httpStatusCodes[row.dataset.statusCode] + "\n\n"; 117 | details.responseHeaders.sort(function(a,b) {return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);}); 118 | for (let i = 0; i < details.responseHeaders.length; i++) { 119 | DOM.dataArea.innerHTML += details.responseHeaders[i].name + ": " + details.responseHeaders[i].value + "\n"; 120 | } 121 | } 122 | } 123 | else if (type === 'o_live_output_raw') { 124 | DOM.dataArea.innerHTML = row.dataset.details; 125 | } 126 | else if (type === 'remove') { 127 | DOM.dataArea.innerHTML = ''; 128 | } 129 | }, 130 | 'stream' : function (n, details, type = null) { 131 | if (type === 'remove') { 132 | DOM.streamData.tBodies[0].innerHTML = ''; 133 | return!1; 134 | } 135 | 136 | let direction = (details.responseHeaders ? "RESP" : "REQ"); 137 | 138 | let html = ''; 139 | html += '' + n + ''; 140 | html += '' + direction + ''; 141 | html += ''; 142 | html += '' + details.method + ''; 143 | if (details.statusCode) { 144 | let sum = 'd04d058e57ce92bf697f7c233a8061bce1210a4b'; 145 | html += '' + details.statusCode + ''; 146 | } 147 | else { 148 | html += ''; 149 | } 150 | html += ''; 151 | 152 | let tr = document.createElement('tr'); 153 | tr.dataset.number = n; 154 | tr.dataset.tab = details.tabId; 155 | tr.dataset.direction = direction; 156 | tr.dataset.requestId = details.requestId; 157 | tr.dataset.type = details.type; 158 | tr.dataset.method = details.method; 159 | tr.dataset.statusCode = details.statusCode || null; 160 | tr.dataset.details = JSON.stringify(details); 161 | tr.innerHTML = html; 162 | tr.addEventListener('click', function (e) { 163 | if (e.ctrlKey) { 164 | console.log('ctrl click'); 165 | } 166 | if (e.shiftKey) { 167 | console.log('shift click'); 168 | } 169 | if (a) { 170 | if (a === this) { 171 | this.classList.remove('stream-active'); 172 | a = null; 173 | s = true; 174 | generate_dom.information(null, 'remove'); 175 | return!1; 176 | } 177 | else { 178 | a.classList.remove('stream-active'); 179 | } 180 | } 181 | this.classList.add('stream-active'); 182 | a = this; 183 | s = false; 184 | generate_dom.information(this, t.id); 185 | }); 186 | 187 | if (document.querySelectorAll('[data-request-id="' + details.requestId + '"][data-status-code="null"]').length === 1) { 188 | document.querySelectorAll('[data-request-id="' + details.requestId + '"][data-status-code="null"]')[0].dataset.statusCode = details.statusCode; 189 | } 190 | 191 | DOM.streamData.tBodies[0].appendChild(tr); 192 | 193 | if (s === true) { 194 | DOM.streamData.scrollIntoView(false); 195 | } 196 | } 197 | }; 198 | 199 | 200 | chrome.webRequest.onHeadersReceived.addListener(function (details) { 201 | if (generate_dom.shouldPrint(details) === false) { 202 | return; 203 | } 204 | else { 205 | n += 1; 206 | generate_dom.stream(n, details); 207 | } 208 | }, { 209 | urls: [""], 210 | types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"] 211 | }, ["blocking", "responseHeaders"]); 212 | 213 | chrome.webRequest.onSendHeaders.addListener(function (details) { 214 | if (generate_dom.shouldPrint(details) === false) { 215 | return; 216 | } 217 | else { 218 | n += 1; 219 | generate_dom.stream(n, details); 220 | } 221 | }, { 222 | urls: [""], 223 | types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"] 224 | }, ["requestHeaders"]); 225 | 226 | 227 | /** 228 | * Create toggleButton 229 | */ 230 | DOM.toggleButton.addEventListener('click', function () { 231 | if (p === false) { 232 | // pause 233 | p = true; 234 | DOM.toggleButton.innerHTML = '►'; 235 | } 236 | else if (p === true) { 237 | // resume 238 | p = false; 239 | DOM.toggleButton.innerHTML = '▮▮'; 240 | } 241 | }); 242 | 243 | 244 | /** 245 | * Create resetButton 246 | */ 247 | DOM.resetButton.addEventListener('click', function () { 248 | // temporary pause 249 | p = true; 250 | 251 | // remove data 252 | generate_dom.stream(null, null, 'remove'); 253 | generate_dom.information(null, 'remove'); 254 | 255 | // reset flags 256 | p = false; // un pause 257 | DOM.toggleButton.innerHTML = '▮▮'; 258 | n = 0; // reset number 259 | a = null; // nullify active row 260 | s = true; // resume scrolling 261 | }); 262 | 263 | 264 | /** 265 | * Information tab change (formatted/raw) 266 | */ 267 | DOM.dataFormattedButton.addEventListener('click', function () { 268 | tabChange(this); 269 | }); 270 | 271 | DOM.dataRawButton.addEventListener('click', function () { 272 | tabChange(this); 273 | }); 274 | 275 | function tabChange (button) { 276 | if (button.id === t.id) { 277 | return!1 278 | } 279 | else { 280 | t.classList.remove('tabber-style-active'); 281 | button.classList.add('tabber-style-active'); 282 | t = button; 283 | if (a) { 284 | generate_dom.information(a, t.id) 285 | } 286 | } 287 | } 288 | 289 | 290 | /** 291 | * Create filterByTabs 292 | */ 293 | chrome.windows.getAll({ populate: true }, function (windows) { 294 | windows.forEach(function (window) { 295 | window.tabs.forEach(function (tab) { 296 | generate_dom.filterByTabs(tab, 'new'); 297 | }); 298 | }); 299 | }); 300 | 301 | chrome.tabs.onCreated.addListener(function (tab) { 302 | generate_dom.filterByTabs(tab, 'new'); 303 | }); 304 | 305 | chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { 306 | if (changeInfo.title) { 307 | generate_dom.filterByTabs(tab, 'update'); 308 | } 309 | }); 310 | 311 | chrome.tabs.onRemoved.addListener(function (tab) { 312 | generate_dom.filterByTabs(tab, 'remove'); 313 | }); 314 | 315 | 316 | /** 317 | * Create filterByStatus 318 | */ 319 | generate_dom.filterByStatus(); 320 | 321 | 322 | /** 323 | * bind_filter_handlers 324 | */ 325 | function bind_filter_handlers () { 326 | /* filterByTabs event */ 327 | DOM.filterByTabs.addEventListener('change', function () { 328 | if (this.options[this.selectedIndex].value === '0') { 329 | DOM.filterByTabs_styleElement.innerHTML = ''; 330 | } 331 | else { 332 | DOM.filterByTabs_styleElement.innerHTML = 'tbody tr:not([data-tab="' + this.options[this.selectedIndex].value + '"]){display:none}'; 333 | } 334 | }); 335 | 336 | /* filterByDirection event */ 337 | for (let i = 0; i < DOM.filterByDirection.length; i++) { 338 | DOM.filterByDirection[i].addEventListener('change', function () { 339 | apply_filters(this); 340 | }); 341 | } 342 | 343 | /* filterByType event */ 344 | for (let i = 0; i < DOM.filterByType.length; i++) { 345 | DOM.filterByType[i].addEventListener('change', function () { 346 | apply_filters(this); 347 | }); 348 | } 349 | 350 | /* filterByMethod event */ 351 | for (let i = 0; i < DOM.filterByMethod.length; i++) { 352 | DOM.filterByMethod[i].addEventListener('change', function () { 353 | apply_filters(this); 354 | }); 355 | } 356 | 357 | /* filterByStatus event */ 358 | DOM.filterByStatus.addEventListener('change', function () { 359 | if (this.options[this.selectedIndex].value === '0') { 360 | DOM.filterByStatus_styleElement.innerHTML = ''; 361 | } 362 | else { 363 | // :not([data-direction="REQ"]) 364 | DOM.filterByStatus_styleElement.innerHTML = 'tbody tr:not([data-status-code="' + this.options[this.selectedIndex].value + '"]){display:none}'; 365 | } 366 | }); 367 | 368 | /* clearFiltersButton event */ 369 | DOM.clearFiltersButton.addEventListener('click', function () { 370 | clear_filters(); 371 | }); 372 | } 373 | bind_filter_handlers(); 374 | 375 | function apply_filters (element, type = null) { 376 | let filters = ''; 377 | let data_name = 'data-' + element.name.replace(/filterBy/,'').toLowerCase(); 378 | 379 | for (let i = 0; i < DOM[element.name].length; i++) { 380 | if (DOM[element.name][i].checked) { 381 | filters += ':not([' + data_name + '="' + DOM[element.name][i].value + '"])'; 382 | } 383 | } 384 | 385 | if (filters != '') { 386 | DOM[element.name + '_styleElement'].innerHTML = 'tbody tr' + filters + '{display:none}'; 387 | } 388 | else { 389 | DOM[element.name + '_styleElement'].innerHTML = ''; 390 | } 391 | } 392 | 393 | function clear_filters () { 394 | for (let i = 0; i < DOM.filterByDirection.length; i++) { 395 | DOM.filterByDirection[i].checked = false; 396 | } 397 | DOM.filterByDirection_styleElement.innerHTML = ''; 398 | 399 | for (let i = 0; i < DOM.filterByType.length; i++) { 400 | DOM.filterByType[i].checked = false; 401 | } 402 | DOM.filterByType_styleElement.innerHTML = ''; 403 | 404 | for (let i = 0; i < DOM.filterByMethod.length; i++) { 405 | DOM.filterByMethod[i].checked = false; 406 | } 407 | DOM.filterByMethod_styleElement.innerHTML = ''; 408 | 409 | DOM.filterByTabs.selectedIndex = 0; 410 | DOM.filterByTabs_styleElement.innerHTML = ''; 411 | 412 | DOM.filterByStatus.selectedIndex = 0; 413 | DOM.filterByStatus_styleElement.innerHTML = ''; 414 | } 415 | 416 | 417 | /** 418 | * Create goToOptionsButton 419 | */ 420 | DOM.goToOptionsButton.addEventListener('click', function () { 421 | if (chrome.runtime.openOptionsPage) { 422 | chrome.runtime.openOptionsPage(); 423 | } else { 424 | window.open(chrome.runtime.getURL('headerOptions.html')); 425 | } 426 | }); 427 | 428 | 429 | /** 430 | * Document Loaded/Ready Functions 431 | */ 432 | document.addEventListener('DOMContentLoaded', function () { 433 | document.body.classList.add(chrome.extension.getBackgroundPage().currentSettings.o_theme); 434 | if (chrome.extension.getBackgroundPage().currentSettings.o_live_donation === 'o_live_donation_hide') { 435 | DOM.donations.style.display = 'none'; 436 | } 437 | 438 | if (t.id === 'o_live_output_formatted') { 439 | DOM.dataFormattedButton.classList.add('tabber-style-active'); 440 | } 441 | else if (t.id === 'o_live_output_raw') { 442 | DOM.dataRawButton.classList.add('tabber-style-active'); 443 | } 444 | }); 445 | -------------------------------------------------------------------------------- /chrome/headersOptions.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | Options • HTTP Headers 15 | 167 | 168 | 169 | 179 | 180 |
181 |
182 |
183 |

Options

184 |
185 |

Theme

186 | 187 | 188 |
189 |
190 |

Default Output on Live Headers View

191 | 192 | 193 |
194 |
195 |

Fetch Direction on Live Headers View

196 | 197 | 198 |
199 |
200 |

Fetch Type on Live Headers View

201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 |
210 |
211 |

Copyright Bar on Live Headers View

212 | 213 | 214 |
215 |
216 | 217 | 218 |
 
219 |
220 |
221 | 222 |
223 |

About HTTP Headers

224 |

225 | HTTP Headers was written when the original HTTP Headers (HTTP Headers Live) extension sold out to malware injection and adverts. I decided it was time to write my own clean, ad-free, safe version that I didn't have to worry about. This is the outcome, slightly different to the original, a way for me to learn as I developed this version. 226 |

227 |

228 | Contact me for bug reports/suggestions; 229 |

230 | 237 |
238 | 239 |
240 |

License

241 |
242 | 	HTTP Headers - https://www.paulhempshall.com/io/http-headers/
243 | 	Copyright (C) 2016-2019, Paul Hempshall. All rights reserved.
244 | 
245 | 	This program is free software: you can redistribute it and/or modify
246 | 	it under the terms of the GNU General Public License as published by
247 | 	the Free Software Foundation, either version 3 of the License, or
248 | 	(at your option) any later version.
249 | 
250 | 	This program is distributed in the hope that it will be useful,
251 | 	but WITHOUT ANY WARRANTY; without even the implied warranty of
252 | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
253 | 	GNU General Public License for more details.
254 | 
255 | 	You should have received a copy of the GNU General Public License
256 | 	along with this program.  If not, see https://opensource.org/licenses/GPL-3.0.
257 | 				
258 |
259 |
260 |
261 | 262 | 267 | 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /chrome/headersOptions.js: -------------------------------------------------------------------------------- 1 | /** 2 | HTTP Headers - https://www.paulhempshall.com/io/http-headers/ 3 | Copyright (C) 2016-2021, Paul Hempshall. All rights reserved. 4 | 5 | This program is distributed in the hope that it will be useful, 6 | but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 8 | 9 | No redistribution without prior written consent. 10 | */ 11 | 12 | 'use strict'; 13 | 14 | // Saves options to chrome.storage 15 | function save_options() { 16 | let newSettings = { 17 | 'o_theme': null, 18 | 'o_live_output': null, 19 | 'o_live_direction': [], 20 | 'o_live_type': [], 21 | 'o_live_donation': null, 22 | }; 23 | 24 | for (let i = 0; i < document.getElementsByName('o_theme').length; i++) { 25 | if (document.getElementsByName('o_theme')[i].checked) { 26 | newSettings.o_theme = document.getElementsByName('o_theme')[i].id; 27 | } 28 | } 29 | for (let i = 0; i < document.getElementsByName('o_live_output').length; i++) { 30 | if (document.getElementsByName('o_live_output')[i].checked) { 31 | newSettings.o_live_output = document.getElementsByName('o_live_output')[i].id; 32 | } 33 | } 34 | for (let i = 0; i < document.getElementsByName('o_live_direction').length; i++) { 35 | if (document.getElementsByName('o_live_direction')[i].checked) { 36 | newSettings.o_live_direction.push(document.getElementsByName('o_live_direction')[i].id); 37 | } 38 | } 39 | for (let i = 0; i < document.getElementsByName('o_live_type').length; i++) { 40 | if (document.getElementsByName('o_live_type')[i].checked) { 41 | newSettings.o_live_type.push(document.getElementsByName('o_live_type')[i].id); 42 | } 43 | } 44 | for (let i = 0; i < document.getElementsByName('o_live_donation').length; i++) { 45 | if (document.getElementsByName('o_live_donation')[i].checked) { 46 | newSettings.o_live_donation = document.getElementsByName('o_live_donation')[i].id; 47 | } 48 | } 49 | 50 | chrome.storage.sync.set( 51 | newSettings, 52 | function() { 53 | let status = document.getElementById('status'); 54 | status.textContent = 'Options saved.'; 55 | setTimeout(function() { 56 | status.innerHTML = ' '; 57 | }, 1550); 58 | } 59 | ); 60 | 61 | chrome.extension.getBackgroundPage().currentSettings = newSettings; 62 | } 63 | 64 | // Restores select box and checkbox state using the preferences 65 | // stored in chrome.storage. 66 | function restore_options() { 67 | chrome.storage.sync.get( 68 | chrome.extension.getBackgroundPage().defaultSettings, 69 | function (settings) { 70 | document.getElementById(settings.o_theme).checked = true; 71 | document.getElementById(settings.o_live_output).checked = true; 72 | for (let i = 0; i < settings.o_live_direction.length; i++) { 73 | document.getElementById(settings.o_live_direction[i]).checked = true; 74 | } 75 | for (let i = 0; i < settings.o_live_type.length; i++) { 76 | document.getElementById(settings.o_live_type[i]).checked = true; 77 | } 78 | document.getElementById(settings.o_live_donation).checked = true; 79 | 80 | chrome.extension.getBackgroundPage().currentSettings = settings; 81 | } 82 | ); 83 | } 84 | 85 | function default_options () { 86 | document.getElementById(chrome.extension.getBackgroundPage().defaultSettings.o_theme).checked = true; 87 | document.getElementById(chrome.extension.getBackgroundPage().defaultSettings.o_live_output).checked = true; 88 | for (let i = 0; i < chrome.extension.getBackgroundPage().defaultSettings.o_live_direction.length; i++) { 89 | document.getElementById(chrome.extension.getBackgroundPage().defaultSettings.o_live_direction[i]).checked = true; 90 | } 91 | for (let i = 0; i < chrome.extension.getBackgroundPage().defaultSettings.o_live_type.length; i++) { 92 | document.getElementById(chrome.extension.getBackgroundPage().defaultSettings.o_live_type[i]).checked = true; 93 | } 94 | document.getElementById(chrome.extension.getBackgroundPage().defaultSettings.o_live_donation).checked = true; 95 | 96 | chrome.storage.sync.set( 97 | chrome.extension.getBackgroundPage().defaultSettings, 98 | function() { 99 | let status = document.getElementById('status'); 100 | status.textContent = 'Default options restored.'; 101 | setTimeout(function() { 102 | status.innerHTML = ' '; 103 | }, 1550); 104 | } 105 | ); 106 | 107 | chrome.extension.getBackgroundPage().currentSettings = chrome.extension.getBackgroundPage().chrome.extension.getBackgroundPage().defaultSettings; 108 | } 109 | 110 | document.addEventListener('DOMContentLoaded', function () { 111 | restore_options(); 112 | document.body.classList.add(chrome.extension.getBackgroundPage().currentSettings.o_theme); 113 | }); 114 | document.getElementById('save').addEventListener('click', function () { 115 | save_options(); 116 | document.body.className = ''; 117 | document.body.classList.add(chrome.extension.getBackgroundPage().currentSettings.o_theme); 118 | }); 119 | document.getElementById('defaults').addEventListener('click', function () { 120 | if (confirm('Are you sure you want to restore the default options?')) { 121 | default_options(); 122 | document.body.className = ''; 123 | document.body.classList.add(chrome.extension.getBackgroundPage().currentSettings.o_theme); 124 | } else { 125 | return!1; 126 | } 127 | }); 128 | -------------------------------------------------------------------------------- /chrome/headersPopup.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | HTTP Headers 15 | 78 | 79 | 80 |

81 | HTTP Headers 82 |

83 | 84 |
85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /chrome/headersPopup.js: -------------------------------------------------------------------------------- 1 | /** 2 | HTTP Headers - https://www.paulhempshall.com/io/http-headers/ 3 | Copyright (C) 2016-2021, Paul Hempshall. All rights reserved. 4 | 5 | This program is distributed in the hope that it will be useful, 6 | but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 8 | 9 | No redistribution without prior written consent. 10 | */ 11 | 12 | 'use strict'; 13 | 14 | chrome.tabs.query({active: true, currentWindow: true}, function(tab) { 15 | var results = document.getElementById('results'), 16 | headers = chrome.extension.getBackgroundPage().headers[tab[0].id]; 17 | 18 | document.body.classList.add(chrome.extension.getBackgroundPage().currentSettings.o_theme); 19 | 20 | if (headers === undefined) { 21 | printError(); 22 | } 23 | else { 24 | printResults(); 25 | } 26 | 27 | function sanitize (data) { 28 | data = data.replace(//g, ">"); 30 | return data; 31 | } 32 | 33 | function clearResults () { 34 | results.innerHTML = ''; 35 | } 36 | 37 | function printError () { 38 | var error = "Error: could not get http headers, please try refreshing the page."; 39 | 40 | clearResults(); 41 | results.innerHTML += "

" + error + "

" 42 | } 43 | 44 | function printResults () { 45 | clearResults(); 46 | 47 | // print request 48 | headers['request']['requestHeaders'].sort(function(a,b) {return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);}); 49 | printHeading('request'); 50 | printKeys('request'); 51 | 52 | // print response 53 | headers['response']['responseHeaders'].sort(function(a,b) {return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);}); 54 | printHeading('response'); 55 | printKeys('response'); 56 | 57 | 58 | function calcTime () { 59 | return ((headers['response'].timeStamp - headers['request'].timeStamp) / 1000).toFixed(4); 60 | } 61 | 62 | function printHeading (key) { 63 | var t = key[0].toUpperCase() + key.substring(1); 64 | 65 | if (key === 'response') { 66 | results.innerHTML += "

" + t + " (in " + calcTime() + "s)

"; 67 | } 68 | else { 69 | results.innerHTML += "

" + t + "

"; 70 | } 71 | } 72 | 73 | function printStatus (obj) { 74 | if (obj.statusLine) { 75 | results.innerHTML += "

" + sanitize(obj.statusLine) + "

"; 76 | } 77 | else { 78 | results.innerHTML += "

" + sanitize(obj.method) + " " + sanitize(obj.url) + "

"; 79 | } 80 | } 81 | 82 | function printHeader (obj) { 83 | results.innerHTML += "

" + sanitize(obj.name) + ": " + sanitize(obj.value) + "

"; 84 | } 85 | 86 | function printKeys (key) { 87 | for (var i = 0; i < headers[key][key + 'Headers'].length; i++) { 88 | if (i === 0) { 89 | printStatus(headers[key]); 90 | } 91 | printHeader(headers[key][key + 'Headers'][i]); 92 | } 93 | } 94 | } 95 | }); 96 | 97 | 98 | document.getElementById('live_headers_link').addEventListener('click', launchLiveHeaders, false); 99 | 100 | function launchLiveHeaders (e) { 101 | e.preventDefault(); 102 | 103 | chrome.tabs.create( 104 | { 105 | url: chrome.extension.getURL("headersLive.html") 106 | } 107 | ); 108 | } 109 | -------------------------------------------------------------------------------- /chrome/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phempshall/http-headers/3e5695dcc8a908756ca77cdca24c6faf562e9793/chrome/icon-128.png -------------------------------------------------------------------------------- /chrome/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phempshall/http-headers/3e5695dcc8a908756ca77cdca24c6faf562e9793/chrome/icon-16.png -------------------------------------------------------------------------------- /chrome/icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phempshall/http-headers/3e5695dcc8a908756ca77cdca24c6faf562e9793/chrome/icon-256.png -------------------------------------------------------------------------------- /chrome/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phempshall/http-headers/3e5695dcc8a908756ca77cdca24c6faf562e9793/chrome/icon-32.png -------------------------------------------------------------------------------- /chrome/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phempshall/http-headers/3e5695dcc8a908756ca77cdca24c6faf562e9793/chrome/icon-48.png -------------------------------------------------------------------------------- /chrome/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phempshall/http-headers/3e5695dcc8a908756ca77cdca24c6faf562e9793/chrome/icon-64.png -------------------------------------------------------------------------------- /chrome/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "author": "Paul Hempshall", 4 | 5 | "name": "HTTP Headers", 6 | "version": "1.0.5", 7 | "description": "Displays the current tab’s HTTP request and response or a Live HTTP Headers tab showing a stream of requests and responses.", 8 | 9 | "background": { 10 | "page": "headersBackground.html" 11 | }, 12 | 13 | "browser_action": { 14 | "default_title": "HTTP Headers", 15 | "default_popup": "headersPopup.html" 16 | }, 17 | 18 | "options_ui": { 19 | "page": "headersOptions.html", 20 | "open_in_tab": true 21 | }, 22 | 23 | "icons": { 24 | "16": "icon-16.png", 25 | "32": "icon-32.png", 26 | "48": "icon-48.png", 27 | "64": "icon-64.png", 28 | "128": "icon-128.png", 29 | "256": "icon-256.png" 30 | }, 31 | 32 | "permissions": [ 33 | "webRequest", 34 | "webRequestBlocking", 35 | "http://*/*", 36 | "https://*/*", 37 | "tabs", 38 | "storage" 39 | ] 40 | } --------------------------------------------------------------------------------