Add path normalization

This commit is contained in:
Juhani Haverinen 2016-08-02 01:33:02 +03:00
parent a7c9f89581
commit cf66f1eee2
1 changed files with 40 additions and 2 deletions

View File

@ -93,6 +93,38 @@ def extract_selector_path(selector_path, *, config):
return selector, path
class PathError(OneArgumentException):
text = 'Error with request path: %s'
# normalize_path(path, *, config) → normalized_path
# Normalize the path or raise an exception if the path is malformed
def normalize_path(path, *, config):
path_components = path.split('/')
normalized_components = []
for component in path_components:
if component == '':
# A dummy left by // or / in beginning or end, ignore
continue
elif component == '.':
# foo/. = foo, ./bar = bar, ignore
continue
elif component == '..':
# foo/bar/.. = foo, drop last component
# This equality does not always hold in a real unix system. However, there are two reasons these semantics are used
# 1. Gopher has no concept of symlinks, and many clients have "parent directory" option that drops last component of path
# 2. This allows for safe usage of symlinks in gopherroot to outside of it, rogue request can't escape to parent directory
if len(normalized_components) > 0: # Ensure we have a component to drop and drop it
normalized_components.pop()
else:
# Attempted .. on an empty path, means attempting to point outside gopherroot
raise PathError('Path points outside gopherroot')
else:
# A normal path component, add to the normalized path
normalized_components.append(component)
return '/'.join(normalized_components)
class Protocol(enum.Enum):
gopher, http = range(2)
@ -129,13 +161,19 @@ def get_request(sock, *, config):
if len(first_line) >= 2 and first_line[0] == 'GET':
selector_path = first_line[1]
selector, path = extract_selector_path(selector_path, config = config)
return path, Protocol.http, selector
protocol = Protocol.http
rest = selector
else:
if len(first_line) >= 1:
path = first_line[0]
else:
path = ''
return path, Protocol.gopher, None
protocol = Protocol.gophrt
rest = None
path = normalize_path(path, config = config)
return path, Protocol.gopher, None
# Worker thread implementation
class Serve(threading.Thread):