mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 17:12:13 -06:00
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
|
|
}));
|