Commits (15)
...@@ -4,6 +4,12 @@ A browser-based solution to Web censorship, implemented as a JavaScript library ...@@ -4,6 +4,12 @@ A browser-based solution to Web censorship, implemented as a JavaScript library
Ideally, users should not need to install any special software nor change any settings to continue being able to access a blocked Samizdat-enabled site as soon as they are able to access it *once*. Ideally, users should not need to install any special software nor change any settings to continue being able to access a blocked Samizdat-enabled site as soon as they are able to access it *once*.
## Curent status
Samizdat is currently considered *alpha*: the code works, but major rewrites and API changes are coming.
Feel free to test it, but be aware that it might not work as expected. If you'd like to get in touch, please e-mail us at `rysiek+samizdat[at]occrp.org`.
## Rationale ## Rationale
While a number of censorship circumvention technologies exist, these typically require those who want to access the blocked content (readers) to install specific tools (applications, browser extensions, VPN software, etc.), or change their settings (DNS servers, HTTP proxies, etc.). This approach does not scale. While a number of censorship circumvention technologies exist, these typically require those who want to access the blocked content (readers) to install specific tools (applications, browser extensions, VPN software, etc.), or change their settings (DNS servers, HTTP proxies, etc.). This approach does not scale.
......
...@@ -19,6 +19,14 @@ h1 { ...@@ -19,6 +19,14 @@ h1 {
text-align:center; text-align:center;
} }
code {
background: #fff6;
padding: 0.2em 0.5em;
border-radius: 0.4em;
text-shadow: none;
font-weight: bold;
}
#subtitle { #subtitle {
font-style: italic; font-style: italic;
font-variant: super; font-variant: super;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
<script defer src="./lib/gun.js"></script> <script defer src="./lib/gun.js"></script>
<script defer src="./lib/sea.js"></script> <script defer src="./lib/sea.js"></script>
<script defer src="./lib/webrtc.js"></script> <script defer src="./lib/webrtc.js"></script>
<script defer src="./plugins/fetch.js"></script>
<script defer src="./plugins/cache.js"></script> <script defer src="./plugins/cache.js"></script>
<script defer src="./plugins/gun-ipfs.js"></script> <script defer src="./plugins/gun-ipfs.js"></script>
<script> <script>
...@@ -66,7 +67,7 @@ ...@@ -66,7 +67,7 @@
if (typeof fetchedResourcesDisplay !== 'object') { if (typeof fetchedResourcesDisplay !== 'object') {
fetchedResourcesDisplay = document.getElementById("fetched-resources-list") fetchedResourcesDisplay = document.getElementById("fetched-resources-list")
} }
var itemHTML = `<li class="fetched-resources-item"><label><input type="checkbox" checked="checked"/><span class="fetched-resource-url"><span>${si.url}</span></span><span class="fetched-resource-method fetch ${(si.method === 'fetch') ? 'active' : ''}">fetch</span>` var itemHTML = `<li class="fetched-resources-item"><label><input type="checkbox" checked="checked"/><span class="fetched-resource-url"><span>${si.url}</span></span>`
SamizdatPlugins.forEach((plugin)=>{ SamizdatPlugins.forEach((plugin)=>{
var pclass = samizdat.safeClassName(plugin.name); var pclass = samizdat.safeClassName(plugin.name);
itemHTML += `<span class="fetched-resource-method ${pclass} ${(si.method === plugin.name) ? 'active' : ''}">${plugin.name}</span>` itemHTML += `<span class="fetched-resource-method ${pclass} ${(si.method === plugin.name) ? 'active' : ''}">${plugin.name}</span>`
...@@ -183,33 +184,35 @@ ...@@ -183,33 +184,35 @@
/** /**
* adding certain resources from cache * stashing and unstashing resources
*
* stash param means "stash" if set to true (the default), "unstash" otherwise
*/ */
samizdat.addResourcesToCache = () => { samizdat.stashOrUnstashResources = (stash=true) => {
caches.open('v1') // what are we doing?
.then((cache) => { if (stash) {
var resources = [] var operation = 'stash'
document.querySelectorAll('.fetched-resources-item input:checked') } else {
.forEach((el)=>{ var operation = 'unstash'
resources.push(el.parentElement.querySelector('.fetched-resource-url').innerText) }
}) // get the resources
return SamizdatPlugins[0].push(resources) var resources = []
}) document
} .querySelectorAll('.fetched-resources-item input:checked')
.forEach((el)=>{
/** resources.push(el.parentElement.querySelector('.fetched-resource-url').innerText)
* removing certain resources from cache })
*/ // cycle through plugins and find the first that implements a stash() method
samizdat.clearResourcesFromCache = () => { for (i=0; i<SamizdatPlugins.length; i++) {
caches.open('v1') if (typeof SamizdatPlugins[i][operation] === 'function') {
.then((cache) => { console.log('(COMMIT_UNKNOWN) Using plugin "' + SamizdatPlugins[i].name + '" to ' + operation + ' the resources...')
document.querySelectorAll('.fetched-resources-item input:checked') return SamizdatPlugins[i][operation](resources)
.forEach((el)=>{ }
cache.delete(el.parentElement.querySelector('.fetched-resource-url').innerText) }
}) // if we're here that means there was no plugin able to stash things
}) return Promise.reject(new Error('No stashing plugin found'))
} }
/** /**
* publishing certain resources to Gun+IPFS * publishing certain resources to Gun+IPFS
...@@ -225,15 +228,11 @@ ...@@ -225,15 +228,11 @@
.forEach((el)=>{ .forEach((el)=>{
resources.push(el.parentElement.querySelector('.fetched-resource-url').innerText) resources.push(el.parentElement.querySelector('.fetched-resource-url').innerText)
}) })
return SamizdatPlugins[1].push(resources, user, pass) return SamizdatPlugins[1].publish(resources, user, pass)
} }
// add plugin status display // add plugin status display
samizdat.addPluginStatus({
name: 'fetch',
description: 'Regular HTTP(S) fetch()'
})
SamizdatPlugins.forEach(samizdat.addPluginStatus) SamizdatPlugins.forEach(samizdat.addPluginStatus)
// TODO: do it better, watch for ongoing requests or some such? // TODO: do it better, watch for ongoing requests or some such?
...@@ -260,12 +259,7 @@ ...@@ -260,12 +259,7 @@
) )
// once we have all the data... // once we have all the data...
.then((val)=>{ .then((val)=>{
// add plugin stats for the regular HTTP(S) fetch() // add plugins' stats
samizdat.updatePluginStatus({
name: 'fetch',
description: 'Regular HTTP(S) fetch()'
})
// and all other plugins
SamizdatPlugins.forEach(samizdat.updatePluginStatus) SamizdatPlugins.forEach(samizdat.updatePluginStatus)
}) })
}, 5000) }, 5000)
...@@ -283,6 +277,7 @@ ...@@ -283,6 +277,7 @@
<p><em>Samizdat</em> is a browser-based Web censorship circumvention library, easily deployable on any website.</p> <p><em>Samizdat</em> is a browser-based Web censorship circumvention library, easily deployable on any website.</p>
<p>Implemented in JavaScript, It uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Service Workers</a> and a set of non-standard in-browser content delivery mechanisms (with strong focus on decentralized ones, like <a href="https://gun.eco/">Gun</a>, and <a href="https://github.com/ipfs/js-ipfs">JS-IPFS</a>).</p> <p>Implemented in JavaScript, It uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Service Workers</a> and a set of non-standard in-browser content delivery mechanisms (with strong focus on decentralized ones, like <a href="https://gun.eco/">Gun</a>, and <a href="https://github.com/ipfs/js-ipfs">JS-IPFS</a>).</p>
<p>Ideally, users would not need to install any special software nor change any settings to continue being able to access a blocked <em>Samizdat</em>-enabled site as soon as they are able to access it <em>once</em>.</p> <p>Ideally, users would not need to install any special software nor change any settings to continue being able to access a blocked <em>Samizdat</em>-enabled site as soon as they are able to access it <em>once</em>.</p>
<p><em>Samizdat</em> is currently considered <code>alpha</code> software. We would love to hear if you'd like to test it &mdash; you can contact us on <code>rysiek+samizdat[at]occrp.org</code>.<p>
</div> </div>
<div id="logo-container"> <div id="logo-container">
<div id="logo"> <div id="logo">
...@@ -299,7 +294,7 @@ ...@@ -299,7 +294,7 @@
<p>The list below contains all resources fetched in relation to this page.</p> <p>The list below contains all resources fetched in relation to this page.</p>
<ul id="fetched-resources-list"></ul> <ul id="fetched-resources-list"></ul>
<p id="fetched-resources-list-empty">The list is empty, but if the Service Worker is running it should be populated soon.</p> <p id="fetched-resources-list-empty">The list is empty, but if the Service Worker is running it should be populated soon.</p>
<p id="fetched-resources-controls"><button type="button" onclick="samizdat.toggleResourceCheckboxes()">Toggle selection</button><span class="spacer"></span><button type="button" onclick="samizdat.addResourcesToCache()">Add selected to cache</button><button type="button" onclick="samizdat.clearResourcesFromCache()">Clear selected from cache</button><span class="spacer"></span><input type="text" placeholder="Gun username" id="samizdat-gun-user"/><input type="password" placeholder="Gun password" id="samizdat-gun-password"/><button type="button" onclick="samizdat.publishResourcesToGunAndIPFS()">Publish to Gun+IPFS</button></p> <p id="fetched-resources-controls"><button type="button" onclick="samizdat.toggleResourceCheckboxes()">Toggle selection</button><span class="spacer"></span><button type="button" onclick="samizdat.stashOrUnstashResources(true)">Add selected to cache</button><button type="button" onclick="samizdat.stashOrUnstashResources(false)">Clear selected from cache</button><span class="spacer"></span><input type="text" placeholder="Gun username" id="samizdat-gun-user"/><input type="password" placeholder="Gun password" id="samizdat-gun-password"/><button type="button" onclick="samizdat.publishResourcesToGunAndIPFS()">Publish to Gun+IPFS</button></p>
</div> </div>
<p id="footer">ServiceWorker: <span id="samizdat-commit-service-worker">NO_INFO</span>&nbsp;::&nbsp;index.html: <span id="samizdat-commit-index-html">COMMIT_UNKNOWN</span><br/>code: <span id="samizdat-code"><a href="https://git.occrp.org/libre/samizdat/">here</a></span>&nbsp;::&nbsp;license: <span id="samizdat-license"><a href="https://git.occrp.org/libre/samizdat/blob/master/LICENSE">AGPL</a></span></p> <p id="footer">ServiceWorker: <span id="samizdat-commit-service-worker">NO_INFO</span>&nbsp;::&nbsp;index.html: <span id="samizdat-commit-index-html">COMMIT_UNKNOWN</span><br/>code: <span id="samizdat-code"><a href="https://git.occrp.org/libre/samizdat/">here</a></span>&nbsp;::&nbsp;license: <span id="samizdat-license"><a href="https://git.occrp.org/libre/samizdat/blob/master/LICENSE">AGPL</a></span></p>
</body> </body>
......
/* ========================================================================= *\
|* === Stashing plugin using the Cache API === *|
\* ========================================================================= */
/** /**
* getting content from cache * getting content from cache
*/ */
...@@ -17,9 +21,13 @@ let getContentFromCache = (url) => { ...@@ -17,9 +21,13 @@ let getContentFromCache = (url) => {
} }
/** /**
* add resources to cache
*
* implements the stash() Samizdat plugin method
*
* accepts either a Response * accepts either a Response
* or a string containing a URL * or a string containing a URL
* or an array of string URLs * or an Array of string URLs
*/ */
let cacheContent = (resource, url) => { let cacheContent = (resource, url) => {
return caches.open('v1') return caches.open('v1')
...@@ -46,6 +54,40 @@ let cacheContent = (resource, url) => { ...@@ -46,6 +54,40 @@ let cacheContent = (resource, url) => {
} }
}) })
} }
/**
* remove resources from cache
*
* implements the unstash() Samizdat plugin method
*
* accepts either a Response
* or a string containing a URL
* or an Array of string URLs
*/
let clearCachedContent = (resource) => {
return caches.open('v1')
.then((cache) => {
if (typeof resource === 'string') {
// assume URL
console.log("(COMMIT_UNKNOWN) deleting a cached URL")
return cache.delete(resource)
} else if (Array.isArray(resource)) {
// assume array of URLs
console.log("(COMMIT_UNKNOWN) deleting an Array of cached URLs")
return Promise.all(
resource.map((res)=>{
return cache.delete(res)
})
)
} else {
// assume a Response
// which means we have an URL in resource.url
console.log("(COMMIT_UNKNOWN) removing a Response from cache: " + resource.url)
return cache.delete(resource.url)
}
})
}
// initialize the SamizdatPlugins array // initialize the SamizdatPlugins array
if (!Array.isArray(self.SamizdatPlugins)) { if (!Array.isArray(self.SamizdatPlugins)) {
...@@ -59,5 +101,6 @@ self.SamizdatPlugins.push({ ...@@ -59,5 +101,6 @@ self.SamizdatPlugins.push({
description: 'Locally cached responses, using the Cache API.', description: 'Locally cached responses, using the Cache API.',
version: 'COMMIT_UNKNOWN', version: 'COMMIT_UNKNOWN',
fetch: getContentFromCache, fetch: getContentFromCache,
push: cacheContent stash: cacheContent,
unstash: clearCachedContent
}) })
/* ========================================================================= *\
|* === Regular HTTP(S) fetch() plugin === *|
\* ========================================================================= */
/**
* this plugin does not implement any push method
*/
/**
* getting content using regular HTTP(S) fetch()
*/
let fetchContent = (url) => {
console.log('Samizdat: regular fetch!')
return fetch(url)
.then((response) => {
// 4xx? 5xx? that's a paddlin'
if (response.status >= 400) {
// throw an Error to fall back to Samizdat:
throw new Error('HTTP Error: ' + response.status + ' ' + response.statusText);
}
// all good, it seems
console.log("(COMMIT_UNKNOWN) Fetched:", response.url);
return response;
})
}
// initialize the SamizdatPlugins array
if (!Array.isArray(self.SamizdatPlugins)) {
self.SamizdatPlugins = new Array()
}
// and add ourselves to it
// with some additional metadata
self.SamizdatPlugins.push({
name: 'fetch',
description: 'Just a regular HTTP(S) fetch()',
version: 'COMMIT_UNKNOWN',
fetch: fetchContent,
})
...@@ -421,5 +421,5 @@ self.SamizdatPlugins.push({ ...@@ -421,5 +421,5 @@ self.SamizdatPlugins.push({
description: 'Decentralized resource fetching using Gun for address resolution and IPFS for content delivery.', description: 'Decentralized resource fetching using Gun for address resolution and IPFS for content delivery.',
version: 'COMMIT_UNKNOWN', version: 'COMMIT_UNKNOWN',
fetch: getContentFromGunAndIPFS, fetch: getContentFromGunAndIPFS,
push: publishContent publish: publishContent
}) })
/* /*
* Samizdat Service Worker. * Samizdat Service Worker.
* *
* Strategy: * Strategy (not fully implemented yet):
* 1. Try to load from main website. * 1. Try to load from main website.
* 2. If loading fails, load from Samizdat. * 2. If loading fails, load from Samizdat.
* 3. If loading is too slow, load from Samizdat. * 3. If loading is too slow, load from Samizdat.
...@@ -18,12 +18,17 @@ if (!Array.isArray(self.SamizdatPlugins)) { ...@@ -18,12 +18,17 @@ if (!Array.isArray(self.SamizdatPlugins)) {
// order in which plugins are loaded defines the order // order in which plugins are loaded defines the order
// in which they are called! // in which they are called!
self.importScripts( self.importScripts(
"./plugins/fetch.js",
"./plugins/cache.js", "./plugins/cache.js",
"./plugins/gun-ipfs.js", "./plugins/gun-ipfs.js",
"./lib/idb-keyval-iife.min.js"); "./lib/idb-keyval-iife.min.js");
console.log('(COMMIT_UNKNOWN) SamizdatPlugins.length:', self.SamizdatPlugins.length) console.log('(COMMIT_UNKNOWN) SamizdatPlugins.length:', self.SamizdatPlugins.length)
/* ========================================================================= *\
|* === SamizdatInfo === *|
\* ========================================================================= */
// initialize the Samizdat info key-value store // initialize the Samizdat info key-value store
if (typeof self.samizdatStore === 'undefined') { if (typeof self.samizdatStore === 'undefined') {
self.samizdatStore = new idbKeyval.Store('samizdat', 'info') self.samizdatStore = new idbKeyval.Store('samizdat', 'info')
...@@ -149,6 +154,158 @@ if (typeof SamizdatInfo !== "object") { ...@@ -149,6 +154,158 @@ if (typeof SamizdatInfo !== "object") {
} }
/* ========================================================================= *\
|* === Main Brain of Samizdat === *|
\* ========================================================================= */
/**
* get a plugin by name
*
* this doesn't have to be super-performant, since we should never have more
* then a few plugins
* (let's see how long it takes for me to eat my own words here)
*/
let getSamizdatPluginByName = (name) => {
for (i=0; i<SamizdatPlugins.length; i++) {
if (SamizdatPlugins[i].name === name) {
return SamizdatPlugins[i]
}
}
return null
}
/**
* run a plugin's fetch() method
* while handling all the auxiliary stuff like saving info in SamizdatInfo
*
* plugin - the plugin to use
* url - string containing the URL to fetch
* lastError - error thrown by the previous plugin, if any (default: null)
* useStashed - use stashed resources; if false, error out on any plugin that implements stash() (default: true)
*/
let samizdatFetch = (plugin, url, lastError=null, useStashed=true) => {
// save info in SamizdatInfo
SamizdatInfo.resources[url].fetchError = lastError;
SamizdatInfo.resources[url].method = plugin.name
// log stuff
console.log("(COMMIT_UNKNOWN) Samizdat handling URL:", url);
console.log('+-- last error : ' + lastError)
console.log('+-- current method : ' + plugin.name)
// do we want to use stashed resources?
// TODO: there's probably a better way of handling that than throwing an Error()
if (typeof plugin.stash === 'function' && ! useStashed) {
throw new Error('Not supposed to use stashed resources.')
}
// run the plugin
return plugin.fetch(url)
}
/**
* Cycles through all the plugins, in the order they got registered,
* and returns a Promise resolving to a Response in case any of the plugins
* was able to get the resource
*
* url - string containing the URL we want to fetch
* useStashed - use stashed resources; if false, only pull resources from live sources
* doStash - stash resources once fetched successfully; if false, do not stash pulled resources automagically
*/
let getResourceThroughSamizdat = (url, useStashed=true, doStash=true) => {
// make sure SamizdatInfo exists for the url
if (typeof SamizdatInfo.resources[url] === 'undefined') {
SamizdatInfo.resources[url] = new SamizdatResourceInfo(url)
console.log('+-- created SamizdatInfo.resources[' + url + ']')
}
/**
* this uses Array.reduce() to chain the SamizdatPlugins[]-generated Promises
* using the Promise the first registered plugin as the default value
*
* see: https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/
*
* this also means that SamizdatPlugins[0].fetch() below will run first
* (counter-intutively!)
*/
return SamizdatPlugins.reduce(
(prevPromise, currentPlugin)=>{
return prevPromise.catch((error)=>{
return samizdatFetch(currentPlugin, url, error.toString(), useStashed)
})
},
// this samizdatFetch() will run first
// all other promises generated by SamizdatPlugins[] will be chained on it
// using the catch() in reduce() above
samizdatFetch(SamizdatPlugins[0], url, null, useStashed)
)
.then((response)=>{
// yes, this returns a promise...
return SamizdatInfo.resources[url].method.then((method)=>{
// get the plugin that was used to fetch content
plugin = getSamizdatPluginByName(method)
// if it's a stashing plugin...
if (typeof plugin.stash === 'function') {
// we obviously do not want to stash
console.log('(COMMIT_UNKNOWN) Not stashing, since resource is already retrieved by a stashing plugin:', url);
// since we got the data from a stashing plugin,
// let's run the rest of plugins in the background to check if we can get a fresher resource
// and stash it in cache for later use
console.log('(COMMIT_UNKNOWN) starting background no-stashed fetch for:', url);
getResourceThroughSamizdat(url, false, true)
// return the response so that stuff can keep happening
return response
// otherwise, let's see if we want to stash
} else if (doStash) {
// find the first stashing plugin
for (i=0; i<SamizdatPlugins.length; i++) {
if (typeof SamizdatPlugins[i].stash === 'function') {
// ok, now we're in business
console.log('(COMMIT_UNKNOWN) Stashing a successful fetch of:', url);
console.log('+-- fetched using :', method)
console.log('+-- stashing using :', SamizdatPlugins[i].name)
// working on clone()'ed response so that the original one is not touched
// TODO: should a failed stashing break the flow here? probably not!
return SamizdatPlugins[i].stash(response.clone(), url)
.then((res)=>{
// original response will be needed further down
return response
})
}
}
}
// if we're here it means we went through the whole list of plugins
// and found not a single stashing plugin
// or we don't want to stash the resources in the first place
// that's fine, but let's make sure the response goes forth
return response
})
})
// a final catch... in case all plugins fail
.catch((err)=>{
console.log("(COMMIT_UNKNOWN) Samizdat also failed completely: ", err);
console.log('+-- URL : ' + url)
// cleanup
SamizdatInfo.resources[url].fetchError = err.toString()
// this is very naïve and should in fact be handled inside the relevant plugin, probably
SamizdatInfo.resources[url].method = null
// rethrow
throw err
})
// this is where we can attach a .then() that will run regardless of the result
// which could then:
// - cache the results or perform some other housekeeping in case of success
// - handle the error in case of failure
}
/* ========================================================================= *\
|* === Setting up the event handlers === *|
\* ========================================================================= */
self.addEventListener('install', event => { self.addEventListener('install', event => {
// TODO: Might we want to have a local cache? // TODO: Might we want to have a local cache?
// "COMMIT_UNKNOWN" will be replaced with commit ID // "COMMIT_UNKNOWN" will be replaced with commit ID
...@@ -178,76 +335,7 @@ self.addEventListener('fetch', event => { ...@@ -178,76 +335,7 @@ self.addEventListener('fetch', event => {
// clean the URL, removing any fragment identifier // clean the URL, removing any fragment identifier
var cleanURL = event.request.url.replace(/#.+$/, ''); var cleanURL = event.request.url.replace(/#.+$/, '');
// make sure SamizdatInfo exists for the cleanURL
if (typeof SamizdatInfo.resources[cleanURL] === 'undefined') {
SamizdatInfo.resources[cleanURL] = new SamizdatResourceInfo(cleanURL)
console.log('+-- created SamizdatInfo.resources[' + cleanURL + ']')
}
// clear the error fetchError info
SamizdatInfo.resources[cleanURL].fetchError = null;
SamizdatInfo.resources[cleanURL].method = 'fetch'; // assume the standard fetch will work
// GET requests to our own domain that are *not* #samizdat-info requests // GET requests to our own domain that are *not* #samizdat-info requests
// get handled by plugins in case of an error // get handled by plugins in case of an error
return void event.respondWith( return void event.respondWith(getResourceThroughSamizdat(cleanURL))
/**
* this uses Array.reduce() to chain the SamizdatPlugins[]-generated Promises
* using the Promise from a basic HTTP(S) fetch() as the default value
*
* see: https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/
*
* this also means that fetch() below will run first (counter-intutively!)
*/
SamizdatPlugins.reduce((prevPromise, nextPlugin)=>{
return prevPromise.catch((error)=>{
// Fall back to Samizdat:
console.log("(COMMIT_UNKNOWN) Samizdat fallback handling after: ", error);
console.log('+-- URL : ' + cleanURL)
console.log('+-- method : ' + nextPlugin.name)
// record the error in SamizdatInfo (as a string)
SamizdatInfo.resources[cleanURL].fetchError = error.toString()
// this is very naïve and should in fact be handled inside the relevant plugin, probably
SamizdatInfo.resources[cleanURL].method = nextPlugin.name;
// use the plugin
return nextPlugin.fetch(cleanURL)
})
// this fetch() will run first
// all other promises generated by SamizdatPlugins[] will be chained using catch() on it
}, fetch(event.request)
.then((response) => {
// that's a paddlin'
if (response.status >= 400) {
// throw an Error to fall back to Samizdat:
throw new Error('HTTP Error: ' + response.status + ' ' + response.statusText);
}
// all good, it seems
console.log("Fetched:", response.url);
return response;
})
)
/*.then((response)=>{
// TODO: this assumes the cache plugin is the first one!
// TODO: we should not be caching a response fetched from cache!
console.log("(COMMIT_UNKNOWN) Caching successful fetch: ", cleanURL);
return SamizdatPlugins[0].push(response.clone(), cleanURL).then((res)=>{
return response
})
})*/
// a final catch... in case all plugins fail
.catch((err)=>{
console.log("(COMMIT_UNKNOWN) Samizdat also failed completely: ", err);
console.log('+-- URL : ' + cleanURL)
// cleanup
SamizdatInfo.resources[cleanURL].fetchError = err.toString()
// this is very naïve and should in fact be handled inside the relevant plugin, probably
SamizdatInfo.resources[cleanURL].method = null
// rethrow
throw err
})
// this is where we can attach a .then() that will run regardless of the result
// which could then:
// - cache the results or perform some other housekeeping in case of success
// - handle the error in case of failure
);
}); });