Introducing Gophersnake ======================= *Written: 2020-04-22 00:44:00 UTC* I finally took the time to read about the Gopher protocol and it turns out that I love it. It was even easier than I thought it would be. I made a *super* basic gopher server which can be found on my [rawtext gitea][1]. Though, I can put the entire contents in here and it's really not that bad. Because at the moment it's only 63 lines. See? --- #!/usr/bin/env python3 import asyncio import os.path import stat from pathlib import Path GOPHER_ROOT = os.environ.get('GOPHER_ROOT', '/var/gopher') def get_file(filename): # Relative paths are bad. normpath strips interior `/../` out filename = Path(GOPHER_ROOT, os.path.normpath(filename.rstrip()).lstrip('/')).resolve() gophermap = filename / 'gophermap' #print(filename) #print(gophermap) result = None if filename.exists() and filename.is_file(): result = filename elif gophermap.exists(): result = gophermap #print(result) # If there was a result, but it's not world readable, unset the # result. #print(stat.filemode(result.stat().st_mode)) if result and stat.filemode(result.stat().st_mode)[-3] != 'r': result = None return result async def handle_message(reader, writer): data = await reader.read(1024) request = data.decode() addr = writer.get_extra_info('peername') if not request.strip(): resource = get_file('') else: resource = get_file(request) print('returning', resource) if resource is None: writer.write(b'Error: File or directory not found!') else: writer.write(resource.read_bytes()) await writer.drain() writer.close() loop = asyncio.get_event_loop() coro = asyncio.start_server(handle_message, '0.0.0.0', int(os.environ.get('GOPHER_PORT', 70)), loop=loop) server = loop.run_until_complete(coro) print('serving forever') try: loop.run_forever() except KeyboardInterrupt: print('\r^C caught. goodbye!') pass server.close() loop.run_until_complete(server.wait_closed()) loop.close() --- *How cool is that??* Like... I knew gopher was simple, but I didn't know just how simple. Yeah, that's not going to autogen your directory listing, but I could probably add that in 10 lines or so. For my purposes there I really didn't want to do that, though, since it's just going to be a super basic server for my own personal gopher hole. All gopher really is is asking a server for what lives at a path. That's it. There's not even a domain involved. Which is kind of sad because you can't host multiple different domains on one server, but... that's kind of OK. Anyway, this is a server that exists and if it is helpful for you at all, great! If not... well, I hope you learned anything? [1]: https://git.rawtext.club/wangofett/gophersnake