Add support for HTTP HEAD
This commit is contained in:
parent
f553d18ab1
commit
3a01c99128
1 changed files with 27 additions and 18 deletions
41
neomi.py
41
neomi.py
|
@ -246,12 +246,13 @@ class RequestError(OneArgumentException):
|
||||||
class Protocol(enum.Enum):
|
class Protocol(enum.Enum):
|
||||||
gopher, gopherplus, http = range(3)
|
gopher, gopherplus, http = range(3)
|
||||||
|
|
||||||
# get_request(sock, *, config) → path, protocol, rest
|
# get_request(sock, *, config) → path, protocol, *rest
|
||||||
# Read request from socket and parse it.
|
# Read request from socket and parse it.
|
||||||
# path is the requested path, protocol is Protocol.gopher or Protocol.http depending on the request protocol
|
# path is the requested path, protocol is Protocol.gopher or Protocol.http depending on the request protocol
|
||||||
# rest is protocol-dependant information
|
# rest is protocol-dependant information
|
||||||
def get_request(sockreader, *, config):
|
def get_request(sockreader, *, config):
|
||||||
protocol = None
|
protocol = None
|
||||||
|
just_headers = False
|
||||||
|
|
||||||
request = bytearray()
|
request = bytearray()
|
||||||
|
|
||||||
|
@ -265,16 +266,21 @@ def get_request(sockreader, *, config):
|
||||||
raise RequestError('Request too long')
|
raise RequestError('Request too long')
|
||||||
|
|
||||||
# We have enough data to recognise a HTTP request
|
# We have enough data to recognise a HTTP request
|
||||||
if protocol is None and len(request) >= 4:
|
if protocol is None and len(request) >= 5:
|
||||||
# Does it look like a HTTP GET request?
|
# Does it look like a HTTP GET request?
|
||||||
if request[:3] == bytearray(b'GET') and chr(request[3]) in [' ', '\r', '\t']:
|
if request[:3] == b'GET' and chr(request[3]) in [' ', '\r', '\t']:
|
||||||
# Yes, mark HTTP as protocol
|
# Yes, mark HTTP as protocol
|
||||||
protocol = Protocol.http
|
protocol = Protocol.http
|
||||||
|
# Does it look like a HTTP HEAD request?
|
||||||
|
elif request[:4] == b'HEAD' and chr(request[4]) in [' ', '\r', '\t']:
|
||||||
|
# Yes, mark HTTP as the protocol and that we'll only return the headers
|
||||||
|
protocol = Protocol.http
|
||||||
|
just_headers = True
|
||||||
else:
|
else:
|
||||||
# No, mark Gopher as protocol
|
# No, mark Gopher as protocol
|
||||||
protocol = Protocol.gopher
|
protocol = Protocol.gopher
|
||||||
|
|
||||||
# End of line reached before a HTTP GET request found, mark Gopher as protocol
|
# End of line reached before a HTTP GET or HEAD request found, mark Gopher as protocol
|
||||||
if protocol is None and len(request) >= 1 and request[-1:] == bytearray(b'\n'):
|
if protocol is None and len(request) >= 1 and request[-1:] == bytearray(b'\n'):
|
||||||
protocol = Protocol.gopher
|
protocol = Protocol.gopher
|
||||||
|
|
||||||
|
@ -296,8 +302,8 @@ def get_request(sockreader, *, config):
|
||||||
|
|
||||||
if protocol == Protocol.http:
|
if protocol == Protocol.http:
|
||||||
length = len(request)
|
length = len(request)
|
||||||
# Start after GET
|
# Start after GET/HEAD
|
||||||
index = 3
|
index = 4 if just_headers else 3
|
||||||
# Skip witespace
|
# Skip witespace
|
||||||
while index < length and chr(request[index]) in [' ', '\r', '\n', '\t']: index += 1
|
while index < length and chr(request[index]) in [' ', '\r', '\n', '\t']: index += 1
|
||||||
# Found the start of the requested path
|
# Found the start of the requested path
|
||||||
|
@ -310,8 +316,6 @@ def get_request(sockreader, *, config):
|
||||||
selector_path = urllib.parse.unquote(request[path_start:path_end].decode('utf-8'))
|
selector_path = urllib.parse.unquote(request[path_start:path_end].decode('utf-8'))
|
||||||
selector, path = extract_selector_path(selector_path, config = config)
|
selector, path = extract_selector_path(selector_path, config = config)
|
||||||
|
|
||||||
rest = selector
|
|
||||||
|
|
||||||
# Try to extract user agent
|
# Try to extract user agent
|
||||||
useragent = None
|
useragent = None
|
||||||
for line in request.split(b'\n'):
|
for line in request.split(b'\n'):
|
||||||
|
@ -323,8 +327,10 @@ def get_request(sockreader, *, config):
|
||||||
useragent = line[len(ua_string):].decode('latin-1')
|
useragent = line[len(ua_string):].decode('latin-1')
|
||||||
useragent = useragent.strip()
|
useragent = useragent.strip()
|
||||||
|
|
||||||
|
rest = (selector, just_headers, useragent)
|
||||||
|
|
||||||
elif protocol == Protocol.gopher:
|
elif protocol == Protocol.gopher:
|
||||||
rest = None
|
rest = ()
|
||||||
|
|
||||||
length = len(request)
|
length = len(request)
|
||||||
index = 0
|
index = 0
|
||||||
|
@ -348,15 +354,12 @@ def get_request(sockreader, *, config):
|
||||||
if len(field) >= 1 and field[0] in ['+', '!', '$']:
|
if len(field) >= 1 and field[0] in ['+', '!', '$']:
|
||||||
# It was Gopher+, let's update protocol value and stash the field into rest
|
# It was Gopher+, let's update protocol value and stash the field into rest
|
||||||
protocol = Protocol.gopherplus
|
protocol = Protocol.gopherplus
|
||||||
rest = field
|
rest = (field,)
|
||||||
|
|
||||||
# No useragents in gopher
|
|
||||||
useragent = None
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
unreachable()
|
unreachable()
|
||||||
|
|
||||||
return path, protocol, useragent, rest
|
return (path, protocol) + rest
|
||||||
|
|
||||||
infofiles_cached = set()
|
infofiles_cached = set()
|
||||||
infofiles_cached_lock = threading.Lock()
|
infofiles_cached_lock = threading.Lock()
|
||||||
|
@ -666,7 +669,11 @@ class Serve(threading.Thread):
|
||||||
def handle_request(self):
|
def handle_request(self):
|
||||||
sockreader = SocketReader(self.sock)
|
sockreader = SocketReader(self.sock)
|
||||||
|
|
||||||
path_raw, protocol, useragent, rest = get_request(sockreader, config = self.config)
|
path_raw, protocol, *rest = get_request(sockreader, config = self.config)
|
||||||
|
|
||||||
|
just_headers = False
|
||||||
|
if protocol == Protocol.http:
|
||||||
|
selector, just_headers, useragent = rest
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if is_hurl_path(path_raw):
|
if is_hurl_path(path_raw):
|
||||||
|
@ -689,6 +696,7 @@ class Serve(threading.Thread):
|
||||||
log('%s [%s]: Requested path not found: %s' % (self.address, protocol.name, path_raw))
|
log('%s [%s]: Requested path not found: %s' % (self.address, protocol.name, path_raw))
|
||||||
reader = StringReader('%s not found\n' % path_raw)
|
reader = StringReader('%s not found\n' % path_raw)
|
||||||
send_header(self.sock, protocol, Status.notfound, 'text/plain', config = self.config)
|
send_header(self.sock, protocol, Status.notfound, 'text/plain', config = self.config)
|
||||||
|
if not just_headers:
|
||||||
send_file(self.sock, reader, protocol, 'text/plain', config = self.config)
|
send_file(self.sock, reader, protocol, 'text/plain', config = self.config)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -696,6 +704,7 @@ class Serve(threading.Thread):
|
||||||
reader = FileReader(file)
|
reader = FileReader(file)
|
||||||
|
|
||||||
send_header(self.sock, protocol, Status.ok, mimetype, config = self.config)
|
send_header(self.sock, protocol, Status.ok, mimetype, config = self.config)
|
||||||
|
if not just_headers:
|
||||||
send_file(self.sock, reader, protocol, mimetype, config = self.config)
|
send_file(self.sock, reader, protocol, mimetype, config = self.config)
|
||||||
|
|
||||||
file.close()
|
file.close()
|
||||||
|
@ -706,7 +715,7 @@ class Serve(threading.Thread):
|
||||||
send_file(self.sock, reader, protocol, 'text/plain', config = self.config)
|
send_file(self.sock, reader, protocol, 'text/plain', config = self.config)
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
if useragent is not None:
|
if protocol == Protocol.http:
|
||||||
log('User agent: %s' % useragent)
|
log('User agent: %s' % useragent)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
Loading…
Reference in a new issue