197 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ##
 | |
| ## up2k-php protocol
 | |
| 
 | |
| client initiates handshake:
 | |
| POST text/plain;charset=UTF-8
 | |
| {"name":"pokemon.webm","size":2505628,"hash":["fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0","d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4","Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s"]}
 | |
| 	"name": "pokemon.webm",
 | |
| 	"size": "2505628",
 | |
| 	"hash": [
 | |
| 		"fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0",
 | |
| 		"d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4",
 | |
| 		"Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s"
 | |
| 	],
 | |
| 
 | |
| server creates session id and replies with the same json:
 | |
| 	msg['hash'] = base64(sha256('\n'.join[
 | |
| 		secretsalt, name, size, *hash
 | |
| 	]))[:32].replace('+','-').replace('/','_')
 | |
| 
 | |
| cilent uploads each chunk:
 | |
| POST application/octet-stream
 | |
| X-Up2k-Hash: fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0
 | |
| X-Up2k-Wark: CVNt9EYhgTFHU3xiK6gL-0ciJFopshvo
 | |
| Content-Length: 1048576
 | |
| 
 | |
| server reads wark.txt and checks that hash is expected,
 | |
| writes each POST to "part/{$wark}/{$hash}"
 | |
| replies 200 OK then verifies hash
 | |
| creates flagfile partfile.ok
 | |
| 
 | |
| client does the handshake again,
 | |
| server replies with list of all missing or corrupt chunks,
 | |
| combines parts into final file if all-ok
 | |
| 
 | |
| 
 | |
| 
 | |
| ##
 | |
| ## differences in this impl
 | |
| 
 | |
| use sha512 instead of sha256 everywhere
 | |
| write directly to .$wark.tmp instead of parts, then move to destination
 | |
| may have to forego the predictable wark and instead use random key:
 | |
|   webkit is doing the https-only meme for crypto.subtle.*
 | |
|   so native sha512 is unavailable on LAN (oh no)
 | |
|   so having the client hash everything before first byte is NG
 | |
| 
 | |
| 
 | |
| 
 | |
| ##
 | |
| ## copyparty approach
 | |
| 
 | |
| up2k-registry keeps track of warks and chunks
 | |
| serialize to disk periodically and on shutdown
 | |
| all incoming up2k POSTs are announced to registry at start and finish
 | |
| registry moves file into place when all chunks are verified
 | |
| 
 | |
| 
 | |
| 
 | |
| ##
 | |
| ## in case we can't rely on sha512 of entire file
 | |
| 
 | |
| handshake
 | |
| client gets wark (random session-key)
 | |
| localstorage[filename+size] = wark
 | |
| thread 1: sha512 chunks
 | |
| thread 2: upload chunks
 | |
| server renames wark to filename on last chunk finished
 | |
| if conflict with another wark during upload: all files are renamed
 | |
| if conflict with existing filename: new file is renamed
 | |
| 
 | |
| 
 | |
| 
 | |
| ##
 | |
| ## required capabilities
 | |
| 
 | |
| replace mpsrv with general-purpose broker
 | |
| (ensures synchronous communication with registry from httpsrv)
 | |
| 
 | |
| 
 | |
| 
 | |
| ##
 | |
| ## sample transaction, up2k-php
 | |
| 
 | |
| POST /up/handshake.php HTTP/1.1
 | |
| Content-Type: text/plain;charset=UTF-8
 | |
| Content-Length: 185
 | |
| 
 | |
| {"name":"pokemon.webm","size":2505628,"hash":["fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0","d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4","Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s"]}
 | |
| name	pokemon.webm
 | |
| size	2505628
 | |
| hash	[…]
 | |
| 0	fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0
 | |
| 1	d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4
 | |
| 2	Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s
 | |
| 
 | |
| HTTP/1.1 200 OK
 | |
| Content-Type: text/html; charset=UTF-8
 | |
| 
 | |
| {"name":"pokemon.webm","size":2505628,"hash":["fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0","d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4","Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s"],"wark":"CVNt9EYhgTFHU3xiK6gL-0ciJFopshvo"}
 | |
| name	pokemon.webm
 | |
| size	2505628
 | |
| hash	[…]
 | |
| 0	fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0
 | |
| 1	d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4
 | |
| 2	Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s
 | |
| wark	CVNt9EYhgTFHU3xiK6gL-0ciJFopshvo
 | |
| 
 | |
| POST /up/chunkpit.php HTTP/1.1
 | |
| X-Up2k-Hash: fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0
 | |
| X-Up2k-Wark: CVNt9EYhgTFHU3xiK6gL-0ciJFopshvo
 | |
| Content-Type: application/octet-stream
 | |
| Content-Length: 1048576
 | |
| 
 | |
| HTTP/1.1 200 OK
 | |
| Content-Type: text/html; charset=UTF-8
 | |
| 
 | |
| POST /up/chunkpit.php HTTP/1.1
 | |
| X-Up2k-Hash: d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4
 | |
| X-Up2k-Wark: CVNt9EYhgTFHU3xiK6gL-0ciJFopshvo
 | |
| Content-Type: application/octet-stream
 | |
| Content-Length: 1048576
 | |
| 
 | |
| HTTP/1.1 200 OK
 | |
| Content-Type: text/html; charset=UTF-8
 | |
| 
 | |
| POST /up/chunkpit.php HTTP/1.1
 | |
| X-Up2k-Hash: Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s
 | |
| X-Up2k-Wark: CVNt9EYhgTFHU3xiK6gL-0ciJFopshvo
 | |
| Content-Type: application/octet-stream
 | |
| Content-Length: 408476
 | |
| 
 | |
| HTTP/1.1 200 OK
 | |
| Content-Type: text/html; charset=UTF-8
 | |
| 
 | |
| POST /up/handshake.php HTTP/1.1
 | |
| Content-Type: text/plain;charset=UTF-8
 | |
| Content-Length: 185
 | |
| 
 | |
| {"name":"pokemon.webm","size":2505628,"hash":["fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0","d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4","Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s"]}
 | |
| hash	[…]
 | |
| 0	fUGShzwcSAmw5IbQ3y_2TUrI8a89LYQO-kW0o0rRcU0
 | |
| 1	d5a1aJiv2F3mkf3gUT5ZKddxKyw8R0uv7U4ol_umao4
 | |
| 2	Z0V45L5x9S_djQKeKNRM5FJgiSE0RVQ6_LAi1_CII6s
 | |
| name	pokemon.webm
 | |
| size	2505628
 | |
| 
 | |
| HTTP/1.1 200 OK
 | |
| Content-Type: text/html; charset=UTF-8
 | |
| 
 | |
| {"name":"pokemon.webm","size":2505628,"hash":[],"wark":"CVNt9EYhgTFHU3xiK6gL-0ciJFopshvo"}
 | |
| name	pokemon.webm
 | |
| size	2505628
 | |
| hash	[]
 | |
| wark	CVNt9EYhgTFHU3xiK6gL-0ciJFopshvo
 | |
| 
 | |
| 
 | |
| 
 | |
| ##
 | |
| ## client javascript excerpt
 | |
| 
 | |
| gotfile(ev) 
 | |
| 
 | |
| 		var entry = {
 | |
| 			"n": parseInt(st.files.length.toString()),
 | |
| 			"fobj": fobj,
 | |
| 			"name": fobj.name,
 | |
| 			"size": fobj.size,
 | |
| 			"hash": []
 | |
| 		};
 | |
| 
 | |
| 		st.files.push(entry);
 | |
| 		st.todo.hash.push(entry);
 | |
| 
 | |
| exec_hash()
 | |
| 
 | |
| 		var car = nchunk * chunksize;
 | |
| 		var cdr = car + chunksize;
 | |
| 		reader.readAsArrayBuffer(
 | |
| 			bobslice.call(t.fobj, car, cdr));
 | |
|         
 | |
|         const hashbuf = await crypto.subtle.digest('SHA-256', ev.target.result);
 | |
| 		t.hash.push(buf2b64(hashbuf));
 | |
| 
 | |
| 		st.todo.handshake.push(t);
 | |
| 
 | |
| exec_handshake()
 | |
| 
 | |
| 	var t = st.todo.handshake.shift();
 | |
| 
 | |
| 	xhr.open('POST', 'handshake.php', true);
 | |
| 	xhr.responseType = 'json';
 | |
| 	xhr.send(JSON.stringify({
 | |
| 		"name": t.name,
 | |
| 		"size": t.size,
 | |
| 		"hash": t.hash
 | |
| 	}));
 | 
