bup: alias ?j to request-header Accept: json

and teach PUT to answer in json too
This commit is contained in:
ed 2025-01-10 20:32:12 +00:00
parent 65ce4c90fa
commit ce0e5be406
2 changed files with 37 additions and 20 deletions

View file

@ -1897,7 +1897,7 @@ class HttpCli(object):
return self.handle_stash(False) return self.handle_stash(False)
if "save" in opt: if "save" in opt:
post_sz, _, _, _, path, _ = self.dump_to_file(False) post_sz, _, _, _, _, path, _ = self.dump_to_file(False)
self.log("urlform: %d bytes, %r" % (post_sz, path)) self.log("urlform: %d bytes, %r" % (post_sz, path))
elif "print" in opt: elif "print" in opt:
reader, _ = self.get_body_reader() reader, _ = self.get_body_reader()
@ -1978,11 +1978,11 @@ class HttpCli(object):
else: else:
return read_socket(self.sr, bufsz, remains), remains return read_socket(self.sr, bufsz, remains), remains
def dump_to_file(self, is_put: bool) -> tuple[int, str, str, int, str, str]: def dump_to_file(self, is_put: bool) -> tuple[int, str, str, str, int, str, str]:
# post_sz, sha_hex, sha_b64, remains, path, url # post_sz, halg, sha_hex, sha_b64, remains, path, url
reader, remains = self.get_body_reader() reader, remains = self.get_body_reader()
vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True) vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
rnd, _, lifetime, xbu, xau = self.upload_flags(vfs) rnd, lifetime, xbu, xau = self.upload_flags(vfs)
lim = vfs.get_dbv(rem)[0].lim lim = vfs.get_dbv(rem)[0].lim
fdir = vfs.canonical(rem) fdir = vfs.canonical(rem)
if lim: if lim:
@ -2132,12 +2132,14 @@ class HttpCli(object):
# small toctou, but better than clobbering a hardlink # small toctou, but better than clobbering a hardlink
wunlink(self.log, path, vfs.flags) wunlink(self.log, path, vfs.flags)
halg = "sha512"
hasher = None hasher = None
copier = hashcopy copier = hashcopy
if "ck" in self.ouparam or "ck" in self.headers: if "ck" in self.ouparam or "ck" in self.headers:
zs = self.ouparam.get("ck") or self.headers.get("ck") or "" halg = zs = self.ouparam.get("ck") or self.headers.get("ck") or ""
if not zs or zs == "no": if not zs or zs == "no":
copier = justcopy copier = justcopy
halg = ""
elif zs == "md5": elif zs == "md5":
hasher = hashlib.md5(**USED4SEC) hasher = hashlib.md5(**USED4SEC)
elif zs == "sha1": elif zs == "sha1":
@ -2171,7 +2173,7 @@ class HttpCli(object):
raise raise
if self.args.nw: if self.args.nw:
return post_sz, sha_hex, sha_b64, remains, path, "" return post_sz, halg, sha_hex, sha_b64, remains, path, ""
at = mt = time.time() - lifetime at = mt = time.time() - lifetime
cli_mt = self.headers.get("x-oc-mtime") cli_mt = self.headers.get("x-oc-mtime")
@ -2282,19 +2284,30 @@ class HttpCli(object):
self.args.RS + vpath + vsuf, self.args.RS + vpath + vsuf,
) )
return post_sz, sha_hex, sha_b64, remains, path, url return post_sz, halg, sha_hex, sha_b64, remains, path, url
def handle_stash(self, is_put: bool) -> bool: def handle_stash(self, is_put: bool) -> bool:
post_sz, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put) post_sz, halg, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
spd = self._spd(post_sz) spd = self._spd(post_sz)
t = "%s wrote %d/%d bytes to %r # %s" t = "%s wrote %d/%d bytes to %r # %s"
self.log(t % (spd, post_sz, remains, path, sha_b64[:28])) # 21 self.log(t % (spd, post_sz, remains, path, sha_b64[:28])) # 21
ac = self.uparam.get( mime = "text/plain; charset=utf-8"
"want", self.headers.get("accept", "").lower().split(";")[-1] ac = self.uparam.get("want") or self.headers.get("accept") or ""
) if ac:
ac = ac.split(";", 1)[0].lower()
if ac == "application/json":
ac = "json"
if ac == "url": if ac == "url":
t = url t = url
elif ac == "json" or "j" in self.uparam:
jmsg = {"fileurl": url, "filesz": post_sz}
if halg:
jmsg[halg] = sha_hex[:56]
jmsg["sha_b64"] = sha_b64
mime = "application/json"
t = json.dumps(jmsg, indent=2, sort_keys=True)
else: else:
t = "{}\n{}\n{}\n{}\n".format(post_sz, sha_b64, sha_hex[:56], url) t = "{}\n{}\n{}\n{}\n".format(post_sz, sha_b64, sha_hex[:56], url)
@ -2304,7 +2317,7 @@ class HttpCli(object):
h["X-OC-MTime"] = "accepted" h["X-OC-MTime"] = "accepted"
t = "" # some webdav clients expect/prefer this t = "" # some webdav clients expect/prefer this
self.reply(t.encode("utf-8"), 201, headers=h) self.reply(t.encode("utf-8", "replace"), 201, mime=mime, headers=h)
return True return True
def bakflip( def bakflip(
@ -2983,7 +2996,7 @@ class HttpCli(object):
self.redirect(vpath, "?edit") self.redirect(vpath, "?edit")
return True return True
def upload_flags(self, vfs: VFS) -> tuple[int, bool, int, list[str], list[str]]: def upload_flags(self, vfs: VFS) -> tuple[int, int, list[str], list[str]]:
if self.args.nw: if self.args.nw:
rnd = 0 rnd = 0
else: else:
@ -2991,10 +3004,6 @@ class HttpCli(object):
if vfs.flags.get("rand"): # force-enable if vfs.flags.get("rand"): # force-enable
rnd = max(rnd, vfs.flags["nrand"]) rnd = max(rnd, vfs.flags["nrand"])
ac = self.uparam.get(
"want", self.headers.get("accept", "").lower().split(";")[-1]
)
want_url = ac == "url"
zs = self.uparam.get("life", self.headers.get("life", "")) zs = self.uparam.get("life", self.headers.get("life", ""))
if zs: if zs:
vlife = vfs.flags.get("lifetime") or 0 vlife = vfs.flags.get("lifetime") or 0
@ -3004,7 +3013,6 @@ class HttpCli(object):
return ( return (
rnd, rnd,
want_url,
lifetime, lifetime,
vfs.flags.get("xbu") or [], vfs.flags.get("xbu") or [],
vfs.flags.get("xau") or [], vfs.flags.get("xau") or [],
@ -3057,7 +3065,14 @@ class HttpCli(object):
if not nullwrite: if not nullwrite:
bos.makedirs(fdir_base) bos.makedirs(fdir_base)
rnd, want_url, lifetime, xbu, xau = self.upload_flags(vfs) rnd, lifetime, xbu, xau = self.upload_flags(vfs)
zs = self.uparam.get("want") or self.headers.get("accept") or ""
if zs:
zs = zs.split(";", 1)[0].lower()
if zs == "application/json":
zs = "json"
want_url = zs == "url"
want_json = zs == "json" or "j" in self.uparam
files: list[tuple[int, str, str, str, str, str]] = [] files: list[tuple[int, str, str, str, str, str]] = []
# sz, sha_hex, sha_b64, p_file, fname, abspath # sz, sha_hex, sha_b64, p_file, fname, abspath
@ -3379,7 +3394,7 @@ class HttpCli(object):
msg += "\n" + errmsg msg += "\n" + errmsg
self.reply(msg.encode("utf-8", "replace"), status=sc) self.reply(msg.encode("utf-8", "replace"), status=sc)
elif "j" in self.uparam: elif want_json:
jtxt = json.dumps(jmsg, indent=2, sort_keys=True).encode("utf-8", "replace") jtxt = json.dumps(jmsg, indent=2, sort_keys=True).encode("utf-8", "replace")
self.reply(jtxt, mime="application/json", status=sc) self.reply(jtxt, mime="application/json", status=sc)
else: else:

View file

@ -212,6 +212,7 @@ authenticate using header `Cookie: cppwd=foo` or url param `&pw=foo`
| method | params | body | result | | method | params | body | result |
|--|--|--|--| |--|--|--|--|
| PUT | | (binary data) | upload into file at URL | | PUT | | (binary data) | upload into file at URL |
| PUT | `?j` | (binary data) | ...and reply with json |
| PUT | `?ck` | (binary data) | upload without checksum gen (faster) | | PUT | `?ck` | (binary data) | upload without checksum gen (faster) |
| PUT | `?ck=md5` | (binary data) | return md5 instead of sha512 | | PUT | `?ck=md5` | (binary data) | return md5 instead of sha512 |
| PUT | `?gz` | (binary data) | compress with gzip and write into file at URL | | PUT | `?gz` | (binary data) | compress with gzip and write into file at URL |
@ -236,6 +237,7 @@ upload modifiers:
| http-header | url-param | effect | | http-header | url-param | effect |
|--|--|--| |--|--|--|
| `Accept: url` | `want=url` | return just the file URL | | `Accept: url` | `want=url` | return just the file URL |
| `Accept: json` | `want=json` | return upload info as json; same as `?j` |
| `Rand: 4` | `rand=4` | generate random filename with 4 characters | | `Rand: 4` | `rand=4` | generate random filename with 4 characters |
| `Life: 30` | `life=30` | delete file after 30 seconds | | `Life: 30` | `life=30` | delete file after 30 seconds |
| `CK: no` | `ck` | disable serverside checksum (maybe faster) | | `CK: no` | `ck` | disable serverside checksum (maybe faster) |