##
## 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
	}));
