This repository has been archived by the owner on May 26, 2024. It is now read-only.
forked from iBug/cf-b2cdn
-
Notifications
You must be signed in to change notification settings - Fork 0
/
file_download.js
119 lines (103 loc) · 4.07 KB
/
file_download.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
* Responsible for handling the actual downloading of a requested file from B2.
*/
import {rewriteErrorResponse} from './error_handling';
import {
CACHE_AGE_SECONDS,
PLAIN_TEXT_CONTENT_TYPE,
} from './constants';
/**
* Fetch and return a file from Backblaze B2 by supplying the download authorization
* token in the Authorization header.
* Errors are rewritten to not give away that this is a B2 bucket.
* Headers starting with "x-bz" are removed before being returned to the user.
* Adds cache and security-related headers.
*
* @param request the request from a client that will be rewritten to fetch from
* a Backblaze B2 bucket
* @param {object} b2 the b2config object
* @return {Promise<Response>} the Response to the client that will either be
* the requested file or an error page
*/
async function getB2File(request, b2) {
const requestedUrl = new URL(request.url);
console.log(`requestedUrl = ${requestedUrl.toString()}`);
if (DIR_DOMAIN !== MAIN_DOMAIN && requestedUrl.hostname === DIR_DOMAIN) {
requestedUrl.hostname = MAIN_DOMAIN;
return Response.redirect(requestedUrl.toString(), 301);
}
const url = new URL(b2.data.downloadUrl);
url.pathname = `/file/${B2BUCKET}${requestedUrl.pathname}`;
const response = await fetch(url.toString(), {
cf: {
cacheTtl: 60,
cacheEverything: true,
},
headers: {
'Authorization': b2.data.authorizationToken,
},
});
if (response.ok) {
return modifiedB2Response(request, response);
} else {
return rewriteErrorResponse(request, response);
}
}
/**
* Adds cache headers, converts some Backblaze B2-specific headers to standard
* headers, and deletes the headers that start with "x-bz"
*
* @param request a request for a file on B2
* @param response the successful response from B2 that will be copied and modified
* @param convertHeaders if true, convert x-bz headers to standard headers then delete them
* @return {Promise<Response>} the modified response
*/
async function modifiedB2Response(request, response, convertHeaders=true) {
console.log('modifiedB2Response...');
const newResponse = new Response(response.body, response);
// cache for a week
newResponse.headers.set('Cache-Control', `public, immutable, max-age=${CACHE_AGE_SECONDS}`);
newResponse.headers.set('Expires', new Date(Date.now() + CACHE_AGE_SECONDS * 1000).toUTCString());
if (convertHeaders) {
convertB2Headers(request, newResponse);
}
return newResponse;
}
/**
* Converts the x-bz-content-sha1 header to an ETag header.
* Converts the x-bz-upload-timestamp header to a Last-Modified header.
* By default also deletes all headers that start with "x-bz"
*
* @param request the request from the client for a B2 file
* @param response the response from B2 that will be modified in-place
* @param deleteHeaders if true, delete the x-bz headers in the response
*/
function convertB2Headers(request, response, deleteHeaders=true) {
console.log('convertB2Headers...');
// get a Last-Modified header from x-bz-upload-timestamp
let bzts = response.headers.get('x-bz-upload-timestamp');
bzts = parseInt(bzts);
const d = new Date(bzts);
const lastModified = d.toUTCString();
response.headers.set('Last-Modified', lastModified);
// get an ETag header from x-bz-content-sha1
let bzsha = response.headers.get('x-bz-content-sha1');
bzsha = bzsha.replace(/^unverified:/, ''); // in case it was uploaded without a checksum
bzsha = bzsha.substring(0, 16); // just get the first 16 characters of it
bzsha = `"${bzsha}"`; // CloudFlare wants the ETag wrapped in quotes
response.headers.set('ETag', bzsha);
if (deleteHeaders) {
// remove the 'x-bz-' Backblaze headers
for (const header of response.headers.keys()) {
if (header.match(/^x-bz/i)) {
response.headers.delete(header);
}
}
}
// these file extensions we want to show up as plain text
const url = new URL(request.url);
if (/\.(pub|boot|cfg)$/.test(url.pathname)) {
response.headers.set('Content-Type', PLAIN_TEXT_CONTENT_TYPE);
}
}
export default getB2File;