mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
http error codes
This commit is contained in:
parent
f110d1254d
commit
96c6be0ea1
|
@ -80,10 +80,10 @@ class VFS(object):
|
|||
vn, rem = self._find(vpath)
|
||||
|
||||
if will_read and (uname not in vn.uread and "*" not in vn.uread):
|
||||
raise Pebkac("you don't have read-access for this location")
|
||||
raise Pebkac(403, "you don't have read-access for this location")
|
||||
|
||||
if will_write and (uname not in vn.uwrite and "*" not in vn.uwrite):
|
||||
raise Pebkac("you don't have write-access for this location")
|
||||
raise Pebkac(403, "you don't have write-access for this location")
|
||||
|
||||
return vn, rem
|
||||
|
||||
|
|
|
@ -50,10 +50,10 @@ class HttpCli(object):
|
|||
try:
|
||||
self.mode, self.req, _ = headerlines[0].split(" ")
|
||||
except:
|
||||
raise Pebkac("bad headers:\n" + "\n".join(headerlines))
|
||||
raise Pebkac(400, "bad headers:\n" + "\n".join(headerlines))
|
||||
|
||||
except Pebkac as ex:
|
||||
self.loud_reply(str(ex))
|
||||
self.loud_reply(str(ex), status=ex.code)
|
||||
return False
|
||||
|
||||
self.headers = {}
|
||||
|
@ -107,20 +107,20 @@ class HttpCli(object):
|
|||
elif self.mode == "POST":
|
||||
return self.handle_post()
|
||||
else:
|
||||
raise Pebkac('invalid HTTP mode "{0}"'.format(self.mode))
|
||||
raise Pebkac(400, 'invalid HTTP mode "{0}"'.format(self.mode))
|
||||
|
||||
except Pebkac as ex:
|
||||
try:
|
||||
self.loud_reply(str(ex))
|
||||
self.loud_reply(str(ex), status=ex.code)
|
||||
except Pebkac:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def reply(self, body, status="200 OK", mime="text/html", headers=[]):
|
||||
def reply(self, body, status=200, mime="text/html", headers=[]):
|
||||
# TODO something to reply with user-supplied values safely
|
||||
response = [
|
||||
"HTTP/1.1 " + status,
|
||||
"HTTP/1.1 {} {}".format(status, HTTPCODE[status]),
|
||||
"Connection: Keep-Alive",
|
||||
"Content-Type: " + mime,
|
||||
"Content-Length: " + str(len(body)),
|
||||
|
@ -133,7 +133,7 @@ class HttpCli(object):
|
|||
try:
|
||||
self.s.sendall(response_str + b"\r\n\r\n" + body)
|
||||
except:
|
||||
raise Pebkac("client disconnected before http response")
|
||||
raise Pebkac(400, "client disconnected before http response")
|
||||
|
||||
return body
|
||||
|
||||
|
@ -148,7 +148,7 @@ class HttpCli(object):
|
|||
try:
|
||||
rval = self.headers["range"].split("=", 1)[1]
|
||||
except:
|
||||
rval += self.headers["range"]
|
||||
rval = self.headers["range"]
|
||||
|
||||
logmsg += " [\033[36m" + rval + "\033[0m]"
|
||||
|
||||
|
@ -196,7 +196,7 @@ class HttpCli(object):
|
|||
|
||||
ctype = self.headers.get("content-type", "").lower()
|
||||
if not ctype:
|
||||
raise Pebkac("you can't post without a content-type header")
|
||||
raise Pebkac(400, "you can't post without a content-type header")
|
||||
|
||||
if "multipart/form-data" in ctype:
|
||||
return self.handle_post_multipart()
|
||||
|
@ -207,7 +207,7 @@ class HttpCli(object):
|
|||
if "application/octet-stream" in ctype:
|
||||
return self.handle_post_binary()
|
||||
|
||||
raise Pebkac("don't know how to handle {} POST".format(ctype))
|
||||
raise Pebkac(405, "don't know how to handle {} POST".format(ctype))
|
||||
|
||||
def handle_post_multipart(self):
|
||||
self.parser = MultipartParser(self.log, self.sr, self.headers)
|
||||
|
@ -221,16 +221,16 @@ class HttpCli(object):
|
|||
if act == "login":
|
||||
return self.handle_login()
|
||||
|
||||
raise Pebkac('invalid action "{}"'.format(act))
|
||||
raise Pebkac(422, 'invalid action "{}"'.format(act))
|
||||
|
||||
def handle_post_json(self):
|
||||
try:
|
||||
remains = int(self.headers["content-length"])
|
||||
except:
|
||||
raise Pebkac("you must supply a content-length for JSON POST")
|
||||
raise Pebkac(400, "you must supply a content-length for JSON POST")
|
||||
|
||||
if remains > 1024 * 1024:
|
||||
raise Pebkac("json 2big")
|
||||
raise Pebkac(413, "json 2big")
|
||||
|
||||
enc = "utf-8"
|
||||
ctype = self.headers.get("content-type", "").lower()
|
||||
|
@ -245,7 +245,7 @@ class HttpCli(object):
|
|||
try:
|
||||
body = json.loads(json_buf.decode(enc, "replace"))
|
||||
except:
|
||||
raise Pebkac("you POSTed invalid json")
|
||||
raise Pebkac(422, "you POSTed invalid json")
|
||||
|
||||
print(body)
|
||||
|
||||
|
@ -291,7 +291,7 @@ class HttpCli(object):
|
|||
fn = os.path.join(fdir, sanitize_fn(p_file))
|
||||
|
||||
if not os.path.isdir(fsenc(fdir)):
|
||||
raise Pebkac("that folder does not exist")
|
||||
raise Pebkac(404, "that folder does not exist")
|
||||
|
||||
# TODO broker which avoid this race
|
||||
# and provides a new filename if taken
|
||||
|
@ -303,7 +303,7 @@ class HttpCli(object):
|
|||
self.log("writing to {0}".format(fn))
|
||||
sz, sha512 = hashcopy(self.conn, p_data, f)
|
||||
if sz == 0:
|
||||
raise Pebkac("empty files in post")
|
||||
raise Pebkac(400, "empty files in post")
|
||||
|
||||
files.append([sz, sha512])
|
||||
|
||||
|
@ -365,9 +365,9 @@ class HttpCli(object):
|
|||
|
||||
def tx_file(self, req_path):
|
||||
do_send = True
|
||||
status = "200 OK"
|
||||
status = 200
|
||||
extra_headers = []
|
||||
logmsg = "{:4} {} {}".format("", self.req, status)
|
||||
logmsg = "{:4} {} {}".format("", self.req, "200 OK")
|
||||
|
||||
#
|
||||
# if request is for foo.js, check if we have foo.js.gz
|
||||
|
@ -382,7 +382,7 @@ class HttpCli(object):
|
|||
try:
|
||||
file_sz = os.path.getsize(fsenc(fs_path))
|
||||
except:
|
||||
raise Pebkac("404 Not Found")
|
||||
raise Pebkac(404)
|
||||
|
||||
#
|
||||
# if-modified
|
||||
|
@ -402,7 +402,7 @@ class HttpCli(object):
|
|||
do_send = file_lastmod != cli_lastmod
|
||||
|
||||
if not do_send:
|
||||
status = "304 Not Modified"
|
||||
status = 304
|
||||
|
||||
#
|
||||
# partial
|
||||
|
@ -426,12 +426,12 @@ class HttpCli(object):
|
|||
upper = file_sz
|
||||
|
||||
if lower < 0 or lower >= file_sz or upper < 0 or upper > file_sz:
|
||||
raise Pebkac("na")
|
||||
raise Exception()
|
||||
|
||||
except:
|
||||
self.loud_reply("invalid range requested: " + hrange)
|
||||
raise Pebkac(400, "invalid range requested: " + hrange)
|
||||
|
||||
status = "206 Partial Content"
|
||||
status = 206
|
||||
extra_headers.append(
|
||||
"Content-Range: bytes {}-{}/{}".format(lower, upper - 1, file_sz)
|
||||
)
|
||||
|
@ -469,7 +469,7 @@ class HttpCli(object):
|
|||
mime = mimetypes.guess_type(req_path)[0] or "application/octet-stream"
|
||||
|
||||
headers = [
|
||||
"HTTP/1.1 " + status,
|
||||
"HTTP/1.1 {} {}".format(status, HTTPCODE[status]),
|
||||
"Connection: Keep-Alive",
|
||||
"Content-Type: " + mime,
|
||||
"Content-Length: " + str(upper - lower),
|
||||
|
@ -535,7 +535,7 @@ class HttpCli(object):
|
|||
|
||||
if not os.path.exists(fsenc(abspath)):
|
||||
print(abspath)
|
||||
raise Pebkac("404 not found")
|
||||
raise Pebkac(404)
|
||||
|
||||
if not os.path.isdir(fsenc(abspath)):
|
||||
return self.tx_file(abspath)
|
||||
|
|
|
@ -24,6 +24,21 @@ surrogateescape.register_surrogateescape()
|
|||
FS_ENCODING = sys.getfilesystemencoding()
|
||||
|
||||
|
||||
HTTPCODE = {
|
||||
200: "OK",
|
||||
206: "Partial Content",
|
||||
304: "Not Modified",
|
||||
400: "Bad Request",
|
||||
403: "Forbidden",
|
||||
404: "Not Found",
|
||||
405: "Method Not Allowed",
|
||||
413: "Payload Too Large",
|
||||
422: "Unprocessable Entity",
|
||||
500: "Internal Server Error",
|
||||
501: "Not Implemented",
|
||||
}
|
||||
|
||||
|
||||
class Counter(object):
|
||||
def __init__(self, v=0):
|
||||
self.v = v
|
||||
|
@ -104,12 +119,12 @@ class MultipartParser(object):
|
|||
continue
|
||||
|
||||
if m.group(1).lower() != "form-data":
|
||||
raise Pebkac("not form-data: {}".format(ln))
|
||||
raise Pebkac(400, "not form-data: {}".format(ln))
|
||||
|
||||
try:
|
||||
field = self.re_cdisp_field.match(ln).group(1)
|
||||
except:
|
||||
raise Pebkac("missing field name: {}".format(ln))
|
||||
raise Pebkac(400, "missing field name: {}".format(ln))
|
||||
|
||||
try:
|
||||
fn = self.re_cdisp_file.match(ln).group(1)
|
||||
|
@ -160,7 +175,7 @@ class MultipartParser(object):
|
|||
buf = self.sr.recv(bufsz)
|
||||
if not buf:
|
||||
# abort: client disconnected
|
||||
raise Pebkac("client disconnected during post")
|
||||
raise Pebkac(400, "client disconnected during post")
|
||||
|
||||
while True:
|
||||
ofs = buf.find(self.boundary)
|
||||
|
@ -194,7 +209,7 @@ class MultipartParser(object):
|
|||
buf2 = self.sr.recv(bufsz)
|
||||
if not buf2:
|
||||
# abort: client disconnected
|
||||
raise Pebkac("client disconnected during post")
|
||||
raise Pebkac(400, "client disconnected during post")
|
||||
|
||||
buf += buf2
|
||||
|
||||
|
@ -217,14 +232,14 @@ class MultipartParser(object):
|
|||
return
|
||||
|
||||
if tail != b"\r\n":
|
||||
raise Pebkac("protocol error after field value")
|
||||
raise Pebkac(400, "protocol error after field value")
|
||||
|
||||
def _read_value(self, iterator, max_len):
|
||||
ret = b""
|
||||
for buf in iterator:
|
||||
ret += buf
|
||||
if len(ret) > max_len:
|
||||
raise Pebkac("field length is too long")
|
||||
raise Pebkac(400, "field length is too long")
|
||||
|
||||
return ret
|
||||
|
||||
|
@ -250,7 +265,9 @@ class MultipartParser(object):
|
|||
"""
|
||||
p_field, _, p_data = next(self.gen)
|
||||
if p_field != field_name:
|
||||
raise Pebkac('expected field "{}", got "{}"'.format(field_name, p_field))
|
||||
raise Pebkac(
|
||||
422, 'expected field "{}", got "{}"'.format(field_name, p_field)
|
||||
)
|
||||
|
||||
return self._read_value(p_data, max_len).decode("utf-8", "surrogateescape")
|
||||
|
||||
|
@ -268,7 +285,7 @@ def get_boundary(headers):
|
|||
ct = headers["content-type"]
|
||||
m = re.match(ptn, ct, re.IGNORECASE)
|
||||
if not m:
|
||||
raise Pebkac("invalid content-type for a multipart post: {}".format(ct))
|
||||
raise Pebkac(400, "invalid content-type for a multipart post: {}".format(ct))
|
||||
|
||||
return m.group(2)
|
||||
|
||||
|
@ -293,8 +310,9 @@ def read_header(sr):
|
|||
return None
|
||||
|
||||
raise Pebkac(
|
||||
400,
|
||||
"protocol error while reading headers:\n"
|
||||
+ ret.decode("utf-8", "replace")
|
||||
+ ret.decode("utf-8", "replace"),
|
||||
)
|
||||
|
||||
ret += buf
|
||||
|
@ -424,4 +442,6 @@ def gzip_orig_sz(fn):
|
|||
|
||||
|
||||
class Pebkac(Exception):
|
||||
pass
|
||||
def __init__(self, code, msg=None):
|
||||
super(Pebkac, self).__init__(msg or HTTPCODE[code])
|
||||
self.code = code
|
||||
|
|
Loading…
Reference in a new issue