{"id":20455399,"url":"https://github.com/michael-lazar/flask-gopher","last_synced_at":"2025-04-05T04:15:11.835Z","repository":{"id":57430352,"uuid":"116349172","full_name":"michael-lazar/flask-gopher","owner":"michael-lazar","description":"A Flask extension to support the Gopher protocol","archived":false,"fork":false,"pushed_at":"2025-01-22T22:31:11.000Z","size":1662,"stargazers_count":80,"open_issues_count":3,"forks_count":7,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-29T03:09:55.964Z","etag":null,"topics":["flask","gopher","gopher-protocol","gopher-server"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/michael-lazar.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-05T06:12:47.000Z","updated_at":"2025-01-22T22:31:16.000Z","dependencies_parsed_at":"2025-02-28T14:23:30.196Z","dependency_job_id":null,"html_url":"https://github.com/michael-lazar/flask-gopher","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-lazar%2Fflask-gopher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-lazar%2Fflask-gopher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-lazar%2Fflask-gopher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-lazar%2Fflask-gopher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michael-lazar","download_url":"https://codeload.github.com/michael-lazar/flask-gopher/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247284954,"owners_count":20913704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flask","gopher","gopher-protocol","gopher-server"],"created_at":"2024-11-15T11:18:45.155Z","updated_at":"2025-04-05T04:15:11.815Z","avatar_url":"https://github.com/michael-lazar.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eFlask-Gopher\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003eA \u003ca href=\"http://flask.pocoo.org/\"\u003eFlask\u003c/a\u003e extension to support the \u003ca href=\"https://en.wikipedia.org/wiki/Gopher_(protocol)\"\u003eGopher\u003c/a\u003e protocol.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"gopher\" src=\"resources/gopher_alt.jpg\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pypi.python.org/pypi/flask-gopher/\"\u003e\n    \u003cimg alt=\"pypi\" src=\"https://img.shields.io/pypi/v/flask-gopher.svg?label=version\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://pypi.python.org/pypi/flask-gopher/\"\u003e\n    \u003cimg alt=\"python\" src=\"https://img.shields.io/badge/python-3.7+-blue.svg\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/michael-lazar/flask-gopher/actions\"\u003e\n    \u003cimg alt=\"tests\" src=\"https://github.com/michael-lazar/flask-gopher/workflows/Test/badge.svg\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## Contents\n\n* [Demo](#demo)\n* [About](#about)\n* [Quickstart](#quickstart)\n* [Installation](#installation)\n* [Building Gopher Menus](#building-gopher-menus)\n* [Using Templates](#using-templates)\n* [Gopher and WSGI](#gopher-and-wsgi)\n* [Gopher Protocol References](#gopher-protocol-references)\n\n## Demo\n\nA live demonstration of the Flask-Gopher server is available in\ngopherspace at the following URL:\n\n---\n\n\u003cp align=\"center\"\u003e\n\u003cb\u003e\u003ca href=\"gopher://mozz.us:7005\"\u003egopher://mozz.us:7005\u003c/a\u003e\u003c/b\u003e\u003cbr\u003e\n\u003c/p\u003e\n\n---\n\n## About\n\n*What is gopher?*\n\nGopher is an alternative to the World Wide Web that peaked in\npopularity in the early 90's. There are still a handful of gopher\nsites maintained by enthusiasts; you can learn more about its history\nat [floodgap](http://gopher.floodgap.com/gopher/).\n\n*What is flask-gopher?*\n\nFlask-Gopher is a Flask extension that adds a thin *Gopher -\u003e HTTP*\ncompatability layer around the built-in webserver. It allows you to\nbuild fully [RFC 1466](https://tools.ietf.org/html/rfc1466) compliant\ngopher servers, with complete access to Flask's routing, templating\nengine, debugger, and more!\n\n*Who is this for?*\n\nI created this extension because I wanted to experiment with building\ndynamic gopher applications, and I felt limited by the lack of\nflexibility in other gopher servers. The target audience is web\ndevelopers with experience using a high level web framework like\nDjango or Ruby on Rails. You should feel comfortable writing python\ncode and cross-referencing the official Flask documentation.\n\n## Quickstart\n\n```python\nfrom flask import Flask, url_for\nfrom flask_gopher import GopherExtension, GopherRequestHandler\n\napp = Flask(__name__)\ngopher = GopherExtension(app)\n\n@app.route('/')\ndef index():\n    return gopher.render_menu(\n        gopher.menu.title('My GopherHole'),\n        gopher.menu.dir('Home', url_for('index')),\n        gopher.menu.info(\"Look Ma, it's a gopher server!\"))\n\nif __name__ == '__main__':\n   app.run('127.0.0.1', 70, request_handler=GopherRequestHandler)\n```\n\n## Installation\n\nThis package requires **Python v3.7 or higher**\n\n```\npip install flask_gopher\n```\n\n## Building Gopher Menus\n\nGopher menus are structured text files that display information\nabout the current page and contain links to other gopher resources.\nA gopher menu is loosely equivalent to an HTML document with only\n``\u003ca\u003e`` and ``\u003cspan\u003e`` tags. Each line in the menu has a *type*\nthat describes what kind of resource it links to (text, binary, html,\ntelnet, etc.).\n\nFlask-Gopher provides several helper methods for constructing gopher\nmenu lines:\n\n| Method         | Link Descriptor | Meaning                                 |\n|----------------|-----------------|-----------------------------------------|\n| menu.text      | 0               | Plain text file                         |\n| menu.dir       | 1               | Gopher menu                             |\n| menu.ccso      | 2               | CCSO database; other databases          |\n| menu.error     | 3               | Error message                           |\n| menu.binhex    | 4               | Macintosh BinHex file                   |\n| menu.archive   | 5               | Archive file (zip, tar, gzip)           |\n| menu.uuencoded | 6               | UUEncoded file                          |\n| menu.query     | 7               | Search query                            |\n| menu.telnet    | 8               | Telnet session                          |\n| menu.bin       | 9               | Binary file                             |\n| menu.gif       | g               | GIF format graphics file                |\n| menu.image     | I               | Other Image file                        |\n| menu.doc       | d               | Word processing document (ps, pdf, doc) |\n| menu.sound     | s               | Sound file                              |\n| menu.video     | ;               | Video file                              |\n| menu.info      | i               | Information line                        |\n| menu.title     | i               | Title line                              |\n| menu.html      | h               | HTML document                           |\n\nMost of these methods require a text description for the link, and\nwill accept a path selector and a host/port. They return a line of\ntext that has been pre-formatted for a gopher menu. You can then pass\nall of the lines along into ``gopher.render_menu()`` to build the\nresponse body.\n\n```python\n@app.route('/')\ndef index():\n    return gopher.render_menu(\n        # Link to an internal gopher menu\n        gopher.menu.dir('Home', '/'),\n\n        # Link to an external gopher menu\n        gopher.menu.dir('XKCD comics', '/fun/xkcd', host='gopher.floodgap.com', port=70),\n\n        # Link to a static file, using flask.url_for() to build a relative path\n        gopher.menu.image('Picture of a cat', url_for('static', filename='cat.png')),\n\n        # Link to an external web page\n        gopher.menu.html('Project source', 'https://github.com/michael-lazar/flask-gopher'),\n\n        # A text info line\n        gopher.menu.info('This is informational text'),\n\n        # Plain text will be converted into info lines\n        \"\\n    There's no place\\n    like ::1\\n\",\n\n        # You can also format your links manually\n        \"0About this page\\t/about.txt\\t127.0.0.1\\t8007\")\n```\n\nHere's what the rendered menu looks like as plain text:\n\n```\n$ curl gopher://localhost:8007\n1Home\t/\t127.0.0.1\t8007\n1XKCD comics\t/fun/xkcd\tgopher.floodgap.com\t70\nIPicture of a cat\t/static/cat.png\t127.0.0.1\t8007\nhProject source\tURL:https://github.com/michael-lazar/flask-gopher\t127.0.0.1\t8007\niThis is informational text\tfake\texample.com\t0\ni \tfake\texample.com\t0\ni    There's no place\tfake\texample.com\t0\ni    like ::1\tfake\texample.com\t0\ni \tfake\texample.com\t0\n0About this page\t/about.txt\t127.0.0.1\t8007\n```\n\nAnd here's what it looks like inside of a gopher client:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"gopher\" src=\"resources/example_menu.png\"/\u003e\n\u003c/p\u003e\n\n## Using Templates\n\nYou can use Flask's Jinja2 templating engine to layout gopher menus.\nFlask-Gopher attaches ``gopher`` to the template namespace so you can\naccess the menu helper functions. The recommended naming convention\nfor gopher template files is to add a *.gopher* suffix. An example\ntemplate file is shown below:\n\n**templates/example_menu.gopher**\n\n```\n{{ 'Centered Title' | underline('-') | center }}\n\n{{ gopher.menu.dir('Home', url_for('index')) }}\n\nHello from my gopher template!\nYour IP address is {{ request.remote_addr }}\n\n{{ '_' * gopher.width }}\n{{ ('Served by ' + request.environ['SERVER_SOFTWARE']) | rjust }}\n```\n\nCall ``gopher.render_menu_template()`` from inside of your route to\ncompile the template into a gopher menu.\n\n```python\n@app.route('/')\ndef index():\n    return gopher.render_menu_template('example_menu.gopher')\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"gopher\" src=\"resources/example_template.png\"/\u003e\n\u003c/p\u003e\n\n## Gopher and WSGI\n\nPython's WSGI (Web Server Gateway Interface) is an established API\nthat defines how python web servers (gunicorn, mod_wsgi, etc)\ncommunicate with application frameworks (Flask, Django, etc). It\ndefines a clean boundary between low-level socket and request\nhandling, and high-level application logic.\n\nWSGI was designed to be a very simple and flexible API, but at its\nheart it's built around HTTP requests. As such, it incorperates some\nHTTP specific components like request/response headers and status\ncodes. Gopher is more simplistic and doesn't use these components.\nHere's an example of the difference in fetching a document with the\ntwo protocols:\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=2\u003eHTTP\u003c/th\u003e\u003cth colspan=2\u003eGopher\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003cth\u003erequest\u003c/th\u003e\u003cth\u003eresponse\u003c/th\u003e\u003cth\u003erequest\u003c/th\u003e\u003cth\u003eresponse\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd width=\"20%\"\u003e\u003cpre\u003e\nGET /path HTTP/1.1\nAccept: text/plain\nAccept-Charset: utf-8\n...more headers\n\u003c/pre\u003e\u003c/td\u003e\n\u003ctd width=\"20%\"\u003e\u003cpre\u003e\nHTTP/1.1 200 OK\nServer: Apache\nContent-Type: text/html\n...more headers\u003cbr\u003e\n(body)\n\u003c/pre\u003e\u003c/td\u003e\n\u003ctd width=\"20%\"\u003e\u003cpre\u003e/path\\r\\n\u003c/pre\u003e\u003c/td\u003e\n\u003ctd width=\"20%\"\u003e\u003cpre\u003e(body)\u003c/pre\u003e\u003c/td\u003e\n\u003c/tr\u003e\u003c/table\u003e\n\nIn order to resolve the differences between gopher and HTTP, *\n*Flask-Gopher** implements a custom ``GopherRequestHandler``. The\nhandler hooks into the WSGI server (``werkzeug.BaseWSGIServer``). It\nreads the first line of every TCP connection and determines which\nprotocol the client is attempting to use. If the client is using\ngopher, the following assumptions are made:\n\n- Set the request's *REQUEST_METHOD* to ``GET``\n- Set the request's *SERVER_PROTOCOL* (e.g. *HTTP/1.1*) to ``gopher``\n- Set the request's *wsgi.url_scheme* (e.g. *https*)  to ``gopher``\n- Discard the response status line\n- Discard all response headers\n\nDoing this makes a gopher connection *appear* like a normal HTTP\nrequest from the perspective of the WSGI application. It also provides\nmetadata hooks that can be accessed from the Flask request. For\nexample, you can respond the the request differently depending on\nwhich protocol is being used:\n\n```python\n@app.route('/')\ndef index():\n    if flask.request.scheme == 'gopher':\n        return \"iThis was a gopher request\\tfake\\texample.com\\t0\\r\\n\"\n    else:\n        return \"\u003chtml\u003e\u003cbody\u003eThis was an HTTP request\u003c/body\u003e\u003c/html\u003e\"\n```\n\n## Gopher Protocol References\n\n- https://tools.ietf.org/html/rfc1436 (1993)\n- https://tools.ietf.org/html/rfc4266 (2005)\n- https://tools.ietf.org/html/draft-matavka-gopher-ii-03 (2015)\n- https://www.w3.org/Addressing/URL/4_1_Gopher+.html\n\nAn interesting side note, the python standard library used to contain\nits own gopher module. It was deprecated in 2.5, and removed in\n2.6. (\u003cem\u003ehttps://www.python.org/dev/peps/pep-0004/\u003c/em\u003e)\n\n\n\u003e     Module name:   gopherlib\n\u003e     Rationale:     The gopher protocol is not in active use anymore.\n\u003e     Date:          1-Oct-2000.\n\u003e     Documentation: Documented as deprecated since Python 2.5.  Removed\n\u003e                    in Python 2.6.\n\nA reference gopher client still exists in the old python SVN\ntrunk: https://svn.python.org/projects/python/trunk/Demo/sockets/gopher.py\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichael-lazar%2Fflask-gopher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichael-lazar%2Fflask-gopher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichael-lazar%2Fflask-gopher/lists"}