mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
add HttpConn
This commit is contained in:
parent
e7dc7c9997
commit
c2b270dcea
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,6 +9,7 @@ buildenv/
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
*.rst
|
*.rst
|
||||||
|
.env/
|
||||||
|
|
||||||
# sublime
|
# sublime
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
|
|
@ -10,97 +10,87 @@ import jinja2
|
||||||
from .__init__ import *
|
from .__init__ import *
|
||||||
from .util import *
|
from .util import *
|
||||||
|
|
||||||
if PY2:
|
if not PY2:
|
||||||
from cStringIO import StringIO as BytesIO
|
|
||||||
else:
|
|
||||||
unicode = str
|
unicode = str
|
||||||
from io import BytesIO as BytesIO
|
|
||||||
|
|
||||||
|
|
||||||
class HttpCli(object):
|
class HttpCli(object):
|
||||||
def __init__(self, sck, addr, args, auth, log_func):
|
"""
|
||||||
self.s = sck
|
Spawned by HttpConn to process one http transaction
|
||||||
self.addr = addr
|
"""
|
||||||
self.args = args
|
|
||||||
self.auth = auth
|
|
||||||
|
|
||||||
self.sr = Unrecv(sck)
|
def __init__(self, conn):
|
||||||
|
self.conn = conn
|
||||||
|
self.s = conn.s
|
||||||
|
self.addr = conn.addr
|
||||||
|
self.args = conn.args
|
||||||
|
self.auth = conn.auth
|
||||||
|
|
||||||
|
self.sr = conn.sr
|
||||||
self.bufsz = 1024 * 32
|
self.bufsz = 1024 * 32
|
||||||
self.workload = 0
|
|
||||||
self.ok = True
|
self.ok = True
|
||||||
|
|
||||||
self.log_func = log_func
|
self.log_func = conn.log_func
|
||||||
self.log_src = "{} \033[36m{}".format(addr[0], addr[1]).ljust(26)
|
self.log_src = conn.log_src
|
||||||
|
|
||||||
with open(self.respath("splash.html"), "rb") as f:
|
|
||||||
self.tpl_mounts = jinja2.Template(f.read().decode("utf-8"))
|
|
||||||
|
|
||||||
def respath(self, res_name):
|
|
||||||
return os.path.join(E.mod, "web", res_name)
|
|
||||||
|
|
||||||
def log(self, msg):
|
def log(self, msg):
|
||||||
self.log_func(self.log_src, msg)
|
self.log_func(self.log_src, msg)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while self.ok:
|
try:
|
||||||
try:
|
headerlines = read_header(self.sr)
|
||||||
headerlines = read_header(self.sr)
|
except:
|
||||||
except:
|
return False
|
||||||
self.ok = False
|
|
||||||
return
|
|
||||||
|
|
||||||
self.headers = {}
|
self.headers = {}
|
||||||
try:
|
try:
|
||||||
mode, self.req, _ = headerlines[0].split(" ")
|
mode, self.req, _ = headerlines[0].split(" ")
|
||||||
except:
|
except:
|
||||||
self.log("bad headers:\n" + "\n".join(headerlines))
|
self.log("bad headers:\n" + "\n".join(headerlines))
|
||||||
self.s.close()
|
return False
|
||||||
return
|
|
||||||
|
|
||||||
for header_line in headerlines[1:]:
|
for header_line in headerlines[1:]:
|
||||||
k, v = header_line.split(":", 1)
|
k, v = header_line.split(":", 1)
|
||||||
self.headers[k.lower()] = v.strip()
|
self.headers[k.lower()] = v.strip()
|
||||||
|
|
||||||
self.uname = "*"
|
self.uname = "*"
|
||||||
if "cookie" in self.headers:
|
if "cookie" in self.headers:
|
||||||
cookies = self.headers["cookie"].split(";")
|
cookies = self.headers["cookie"].split(";")
|
||||||
for k, v in [x.split("=", 1) for x in cookies]:
|
for k, v in [x.split("=", 1) for x in cookies]:
|
||||||
if k != "cppwd":
|
if k != "cppwd":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
v = unescape_cookie(v)
|
v = unescape_cookie(v)
|
||||||
if v == "x":
|
if v == "x":
|
||||||
break
|
break
|
||||||
|
|
||||||
if not v in self.auth.iuser:
|
if not v in self.auth.iuser:
|
||||||
msg = u'bad_cpwd "{}"'.format(v)
|
msg = u'bad_cpwd "{}"'.format(v)
|
||||||
nuke = u"Set-Cookie: cppwd=x; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"
|
nuke = u"Set-Cookie: cppwd=x; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"
|
||||||
self.loud_reply(msg, headers=[nuke])
|
self.loud_reply(msg, headers=[nuke])
|
||||||
return
|
return True
|
||||||
|
|
||||||
self.uname = self.auth.iuser[v]
|
self.uname = self.auth.iuser[v]
|
||||||
|
|
||||||
if self.uname:
|
if self.uname:
|
||||||
self.rvol = self.auth.vfs.user_tree(self.uname, readable=True)
|
self.rvol = self.auth.vfs.user_tree(self.uname, readable=True)
|
||||||
self.wvol = self.auth.vfs.user_tree(self.uname, writable=True)
|
self.wvol = self.auth.vfs.user_tree(self.uname, writable=True)
|
||||||
print(self.rvol)
|
print(self.rvol)
|
||||||
print(self.wvol)
|
print(self.wvol)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if mode == "GET":
|
if mode == "GET":
|
||||||
self.handle_get()
|
self.handle_get()
|
||||||
elif mode == "POST":
|
elif mode == "POST":
|
||||||
self.handle_post()
|
self.handle_post()
|
||||||
else:
|
else:
|
||||||
self.loud_reply(u'invalid HTTP mode "{0}"'.format(mode))
|
self.loud_reply(u'invalid HTTP mode "{0}"'.format(mode))
|
||||||
|
|
||||||
except Pebkac as ex:
|
except Pebkac as ex:
|
||||||
self.loud_reply(str(ex))
|
self.loud_reply(str(ex))
|
||||||
|
return False
|
||||||
|
|
||||||
def panic(self, msg):
|
return self.ok
|
||||||
self.log("client disconnected ({0})".format(msg).upper())
|
|
||||||
self.ok = False
|
|
||||||
self.s.close()
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -186,7 +176,7 @@ class HttpCli(object):
|
||||||
|
|
||||||
with open(fn, "wb") as f:
|
with open(fn, "wb") as f:
|
||||||
self.log("writing to {0}".format(fn))
|
self.log("writing to {0}".format(fn))
|
||||||
sz, sha512 = hashcopy(self, p_data, f)
|
sz, sha512 = hashcopy(self.conn, p_data, f)
|
||||||
if sz == 0:
|
if sz == 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -247,5 +237,5 @@ class HttpCli(object):
|
||||||
self.s.send(buf)
|
self.s.send(buf)
|
||||||
|
|
||||||
def tx_mounts(self):
|
def tx_mounts(self):
|
||||||
html = self.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"))
|
||||||
|
|
45
copyparty/httpconn.py
Normal file
45
copyparty/httpconn.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# coding: utf-8
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
from .__init__ import *
|
||||||
|
from .util import Unrecv
|
||||||
|
from .httpcli import HttpCli
|
||||||
|
|
||||||
|
|
||||||
|
class HttpConn(object):
|
||||||
|
"""
|
||||||
|
spawned by HttpSrv to handle an incoming client connection,
|
||||||
|
creates an HttpCli for each request (Connection: Keep-Alive)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, sck, addr, args, auth, log_func):
|
||||||
|
self.s = sck
|
||||||
|
self.addr = addr
|
||||||
|
self.args = args
|
||||||
|
self.auth = auth
|
||||||
|
|
||||||
|
self.sr = Unrecv(sck)
|
||||||
|
self.workload = 0
|
||||||
|
self.ok = True
|
||||||
|
|
||||||
|
self.log_func = log_func
|
||||||
|
self.log_src = "{} \033[36m{}".format(addr[0], addr[1]).ljust(26)
|
||||||
|
|
||||||
|
with open(self.respath("splash.html"), "rb") as f:
|
||||||
|
self.tpl_mounts = jinja2.Template(f.read().decode("utf-8"))
|
||||||
|
|
||||||
|
def respath(self, res_name):
|
||||||
|
return os.path.join(E.mod, "web", res_name)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while True:
|
||||||
|
cli = HttpCli(self)
|
||||||
|
if not cli.run():
|
||||||
|
self.s.close()
|
||||||
|
return
|
||||||
|
|
|
@ -4,13 +4,13 @@ from __future__ import print_function
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from .httpcli import *
|
from .httpconn import *
|
||||||
from .authsrv import *
|
from .authsrv import *
|
||||||
|
|
||||||
|
|
||||||
class HttpSrv(object):
|
class HttpSrv(object):
|
||||||
"""
|
"""
|
||||||
handles incoming connections (parses http and produces responses)
|
handles incoming connections using HttpConn to process http,
|
||||||
relying on MpSrv for performance (HttpSrv is just plain threads)
|
relying on MpSrv for performance (HttpSrv is just plain threads)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -41,20 +41,18 @@ class HttpSrv(object):
|
||||||
|
|
||||||
def thr_client(self, sck, addr, log):
|
def thr_client(self, sck, addr, log):
|
||||||
"""thread managing one tcp client"""
|
"""thread managing one tcp client"""
|
||||||
|
cli = HttpConn(sck, addr, self.args, self.auth, log)
|
||||||
|
with self.mutex:
|
||||||
|
self.clients[cli] = 0
|
||||||
|
self.workload += 50
|
||||||
|
|
||||||
|
if not self.workload_thr_alive:
|
||||||
|
self.workload_thr_alive = True
|
||||||
|
thr = threading.Thread(target=self.thr_workload)
|
||||||
|
thr.daemon = True
|
||||||
|
thr.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# TODO HttpConn between HttpSrv and HttpCli
|
|
||||||
# to ensure no state is kept between http requests
|
|
||||||
cli = HttpCli(sck, addr, self.args, self.auth, log)
|
|
||||||
with self.mutex:
|
|
||||||
self.clients[cli] = 0
|
|
||||||
self.workload += 50
|
|
||||||
|
|
||||||
if not self.workload_thr_alive:
|
|
||||||
self.workload_thr_alive = True
|
|
||||||
thr = threading.Thread(target=self.thr_workload)
|
|
||||||
thr.daemon = True
|
|
||||||
thr.start()
|
|
||||||
|
|
||||||
cli.run()
|
cli.run()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
Loading…
Reference in a new issue