less todo (last-modified / HTTP 304)

This commit is contained in:
ed 2019-06-18 20:23:46 +00:00
parent 5b1e73ff71
commit d59ad1b119
3 changed files with 58 additions and 15 deletions

View file

@ -62,9 +62,7 @@ pip install black bandit pylint flake8 # vscode tooling
roughly sorted by priority roughly sorted by priority
* http error handling (conn.status or handler-retval)
* look into android thumbnail cache file format * look into android thumbnail cache file format
* last-modified header
* support pillow-simd * support pillow-simd
* figure out the deal with pixel3a not being connectable as hotspot * figure out the deal with pixel3a not being connectable as hotspot
* pixel3a having unpredictable 3sec latency in general :|||| * pixel3a having unpredictable 3sec latency in general :||||

View file

@ -8,6 +8,7 @@ __copyright__ = 2019
__license__ = "MIT" __license__ = "MIT"
__url__ = "https://github.com/9001/copyparty/" __url__ = "https://github.com/9001/copyparty/"
import locale
import argparse import argparse
from textwrap import dedent from textwrap import dedent
@ -35,6 +36,18 @@ class RiceFormatter(argparse.HelpFormatter):
def main(): def main():
for x in [
"en_US.UTF-8",
"English_United States.UTF8",
"English_United States.1252",
]:
try:
locale.setlocale(locale.LC_ALL, x)
print("Locale:", x)
break
except:
continue
ap = argparse.ArgumentParser( ap = argparse.ArgumentParser(
formatter_class=RiceFormatter, formatter_class=RiceFormatter,
prog="copyparty", prog="copyparty",

View file

@ -6,6 +6,7 @@ import os
import stat import stat
import time import time
from datetime import datetime from datetime import datetime
import calendar
import mimetypes import mimetypes
import cgi import cgi
@ -45,7 +46,7 @@ class HttpCli(object):
return False return False
try: try:
mode, self.req, _ = headerlines[0].split(" ") self.mode, self.req, _ = headerlines[0].split(" ")
except: except:
raise Pebkac("bad headers:\n" + "\n".join(headerlines)) raise Pebkac("bad headers:\n" + "\n".join(headerlines))
@ -99,12 +100,12 @@ class HttpCli(object):
self.vpath = unquotep(vpath) self.vpath = unquotep(vpath)
try: try:
if mode == "GET": if self.mode in ["GET", "HEAD"]:
return self.handle_get() return self.handle_get()
elif mode == "POST": elif self.mode == "POST":
return self.handle_post() return self.handle_post()
else: else:
raise Pebkac('invalid HTTP mode "{0}"'.format(mode)) raise Pebkac('invalid HTTP mode "{0}"'.format(self.mode))
except Pebkac as ex: except Pebkac as ex:
try: try:
@ -114,6 +115,8 @@ class HttpCli(object):
return False return False
return True
def reply(self, body, status="200 OK", mime="text/html", headers=[]): def reply(self, body, status="200 OK", mime="text/html", headers=[]):
# TODO something to reply with user-supplied values safely # TODO something to reply with user-supplied values safely
response = [ response = [
@ -139,7 +142,7 @@ class HttpCli(object):
self.reply(b"<pre>" + body.encode("utf-8"), *list(args), **kwargs) self.reply(b"<pre>" + body.encode("utf-8"), *list(args), **kwargs)
def handle_get(self): def handle_get(self):
self.log("GET " + self.req) self.log("{:4} {}".format(self.mode, self.req))
# "embedded" resources # "embedded" resources
if self.vpath.startswith(".cpr"): if self.vpath.startswith(".cpr"):
@ -312,15 +315,41 @@ class HttpCli(object):
self.parser.drop() self.parser.drop()
def tx_file(self, path): def tx_file(self, path):
sz = os.path.getsize(fsenc(path)) file_ts = os.path.getmtime(fsenc(path))
mime = mimetypes.guess_type(path)[0] file_dt = datetime.utcfromtimestamp(file_ts)
header = "HTTP/1.1 200 OK\r\nConnection: Keep-Alive\r\nContent-Type: {}\r\nContent-Length: {}\r\n\r\n".format( file_lastmod = file_dt.strftime("%a, %b %d %Y %H:%M:%S GMT")
mime, sz
).encode(
"utf-8"
)
self.s.send(header) do_send = True
if "if-modified-since" in self.headers:
cli_lastmod = self.headers["if-modified-since"]
try:
cli_dt = time.strptime(cli_lastmod, "%a, %b %d %Y %H:%M:%S GMT")
cli_ts = calendar.timegm(cli_dt)
do_send = int(file_ts) > int(cli_ts)
except:
self.log("bad lastmod format: {}".format(cli_lastmod))
do_send = file_lastmod != cli_lastmod
status = "200 OK"
if not do_send:
status = "304 Not Modified"
headers = [
"HTTP/1.1 " + status,
"Connection: Keep-Alive",
"Content-Type: " + mimetypes.guess_type(path)[0],
"Content-Length: " + str(os.path.getsize(fsenc(path))),
"Last-Modified: " + file_lastmod,
]
headers = "\r\n".join(headers).encode("utf-8") + b"\r\n\r\n"
self.s.send(headers)
logmsg = "{:4} {} {}".format("", self.req, status)
if self.mode == "HEAD" or not do_send:
self.log(logmsg)
return True
with open(fsenc(path), "rb") as f: with open(fsenc(path), "rb") as f:
while True: while True:
@ -333,6 +362,9 @@ class HttpCli(object):
except ConnectionResetError: except ConnectionResetError:
return False return False
self.log(logmsg)
return True
def tx_mounts(self): def tx_mounts(self):
html = self.conn.tpl_mounts.render(this=self) html = self.conn.tpl_mounts.render(this=self)
self.reply(html.encode("utf-8")) self.reply(html.encode("utf-8"))