1 | Web server
|
2 | ----------
|
3 |
|
4 | DOWNLOAD = 'get'
|
5 | UPLOAD = 'put'
|
6 | TOKEN = 'token'
|
7 | REV = 'rev'
|
8 | UNTIL = 'until'
|
9 |
|
10 | attachments_proxy = ( our_proxy ) ->
|
11 |
|
12 | {secret,hash,timeout} = our_proxy
|
13 |
|
14 | cors = Cors()
|
15 |
|
16 | hash ?= 'sha256'
|
17 | timeout ?= 3600*1000 # one hour
|
18 |
|
19 | signature = (method,pathname,rev,limit) ->
|
20 | strictEqual 'string', typeof method, 'method'
|
21 | strictEqual 'string', typeof pathname, 'pathname'
|
22 | strictEqual 'string', typeof rev, 'rev'
|
23 | strictEqual 'string', typeof limit, 'limit'
|
24 | crypto
|
25 | .createHmac hash, secret
|
26 | .update method
|
27 | .update pathname
|
28 | .update rev
|
29 | .update limit
|
30 | .digest 'hex'
|
31 |
|
32 | handler = (req,res,next) ->
|
33 |
|
34 | next ?= (msg) ->
|
35 | res.writeHead 404
|
36 | res.end msg
|
37 | return
|
38 |
|
39 | proxy = ->
|
40 | {method} = req
|
41 | options = our_proxy.target pathname
|
42 | return next() unless options?.url?
|
43 |
|
44 | options.url.searchParams.set 'rev', rev
|
45 | options.url = options.url.toString()
|
46 |
|
47 | the_proxy = agent Object.assign {
|
48 | method
|
49 | followRedirects: false
|
50 | maxRedirects: 0
|
51 | headers:
|
52 | Connection: 'close'
|
53 | }, options
|
54 | the_proxy.on 'error', (error) ->
|
55 | console.error 'Proxy', method, options.url, error
|
56 | # req.socket.setKeepAlive true
|
57 | res.setHeader 'Connection', 'close'
|
58 | req.on 'end', -> the_proxy.end()
|
59 | res.on 'close', -> the_proxy.abort() # this is the main one
|
60 | req
|
61 | .pipe the_proxy
|
62 | .pipe res
|
63 | return
|
64 |
|
65 | {pathname,searchParams} = new URL req.url, our_proxy.url
|
66 |
|
67 | token = searchParams.get TOKEN
|
68 | return next 'Invalid token' unless token?
|
69 |
|
70 | rev = searchParams.get REV
|
71 | return next 'Invalid rev' unless rev?
|
72 |
|
73 | limit = searchParams.get UNTIL
|
74 | return next "Invalid limit #{JSON.stringify limit}" unless limit?.match(/^\d+$/) and parseInt(limit) > Date.now()
|
75 |
|
76 | switch req.method
|
77 | when 'OPTIONS'
|
78 | return cors req, res, next
|
79 |
|
80 | when 'PUT'
|
81 | if token is signature UPLOAD, pathname, rev, limit
|
82 | return proxy()
|
83 | else
|
84 | return next "Invalid token"
|
85 |
|
86 | when 'GET', 'HEAD'
|
87 | if token is signature DOWNLOAD, pathname, rev, limit
|
88 | return proxy()
|
89 | else
|
90 | return next "Invalid token"
|
91 |
|
92 | next()
|
93 | return
|
94 |
|
95 | uri_maker = (direction) ->
|
96 | (path,rev) ->
|
97 | url = new URL path, our_proxy.url
|
98 | url.searchParams.set REV, rev
|
99 | limit = Date.now() + timeout
|
100 | url.searchParams.set UNTIL, limit
|
101 | url.searchParams.set TOKEN, signature direction, path, rev, limit.toString()
|
102 | url.toString()
|
103 |
|
104 | download_uri = uri_maker DOWNLOAD
|
105 | upload_uri = uri_maker UPLOAD
|
106 |
|
107 | {handler,download_uri,upload_uri}
|
108 |
|
109 | module.exports = attachments_proxy
|
110 | {URL} = require 'url'
|
111 | crypto = require 'crypto'
|
112 | request = require 'request'
|
113 | agent = request.defaults
|
114 | pool: false
|
115 | Cors = require 'cors'
|
116 | {strictEqual} = assert = require 'assert'
|