⚠️ This post is archived from my phlog in Gopherspace. Please read my post on the Gopher Protocol to get started!
I made a Gopher client called waffle. Basically, a web browser, but for the Gopher Protocol. I started it about 4 years ago now (2020?).
A truly big, complicated beast of a project for me.
I feel like this project touched upon some core areas:
- Rapid acceleration of my Haskell proficiency
- Ability to read and implement RFCs (namely RFC 1436)
- Network programming
- Concurrency
- TUI (I used bricks, I think)
- Large project, requiring careful architecture and separation of concerns
Some other highlights:
- I got to talk to one of the people who worked on the Gopher Protocol (P. Lindner).
- Caching system
- Titlebar thing
- Search
- Profiling for efficiency, working with someone who made a tool I used to analyze profiler output
- Bookmarks
- Color
- UTF-8 support, emojis
- Just using Cabal, basically, not Stack
- Build/run with nix!
Examples of Waffle
I believe Waffle has an INI file for customization:
Playing a hacking game in Waffle on mozz.us:
Waffle playing solitaire:
Waffle playing Zork:
An example of Waffle’s search and refresh abilities (also kind of showing off caching):
The development journey
I found it very satisfying to create a project based around reading an RFC and other research.
Gopher: High Level
The Gopher client, hell, even the Gopher Protocol in general, is very lines-based. As in everything is just composed by a serires of lines, it can feel.
I believe the Gopher Protocol was designed around being simple, easy to hack things together for.
You browse Gopherspace using menus. Menus are basically a bunch of lines that indicate some kind of option you can select to do something, like go to a new directory, download a file, or start a query.
The menus themselves are simple text files that are primarily tab-delimited, looking like this:
1About Gopher /about gopher.server.com 70
0What's New! /whatsnew.txt gopher.server.com 70
1Gopher Clients /clients gopher.server.com 70
1Search Gopher Space /search gopher.server.com 70
0Gopher Protocol Specification /rfc1436.txt gopher.server.com 70
iThis is an information line fake 0
1Fun & Games /fun gopher.server.com 70
1University Information /university gopher.server.com 70
1Libraries around the World /libraries gopher.server.com 70
7Search a gopherhole /search gopher.example.org 70
.
As you can see these menus are pretty easy to parse. Each line represents something actionable. For example if the first line is selected in the client, we know the following information:
- Represent the line as
About Gopher
to the user/client - The line intends to bring the user to another gopher menu (I also see menus
called “directories”), because the first character on the line is
1
, which is the directory (menu) type. Check out RFC1436, 3.8: Item Type Characters for a comprehensive list of such characters. - The “selector string” is
/about
(the path to request) - The server to make the request to is
gopher.server.com
on port70
An i
line is just an information line that’s just there for display. There
are various kinds of downloadable files, like type 9
(binary file) and even a
specific type for GIF (g
).
Maybe the most interesting type is the 7
item type character. It indicates
that the entry intends to perform a query if selected, where a query string is
sent to the server and a response is (hopefully) given based off that query.
Some gopher enthusiasts, myself included, really enjoy abusing this feature for
everything from games (I’ve included some screenshots and even video of games
in Waffle!) to, say, my gopher forum software.
Clients are also kind of expected to view type 0
in the client, I think.
That’s text files.
Interesting bits
- I found it interesting that the way Gopher Clients, Waffle included, tend to get the title bar text or the like, is from the label of the menu item which lead to it.
- I decided to implement a loading screen (there’s a whole interesting
Progress.hs
module), which usesBrick.BChan
, and does some fun networking/socket things - Caching and history
- The TUI/BrickApp gets broken up into different states I call a
ModeAction
, so this could be things like bookmark, help, homepage, menu, open, progress, search, or stuff to do with the menu. The user interface is kind of thought of in relation to a state machine. - INI configuration and storage for bookmarks, styling, default associations, homepage
- Can be piped with tor or whatever, so you can access hidden service Gopherholes
Profiling, benchmarking, optimizing
One of the first major issues I came across was Waffle was extremely slow. I wasn’t sure why, because it felt like I was using brick the way I was supposed to.
I used a terminal-based viewer called
profold for GHC .prof
files generated
by profiling with +RTS -p
, made by someone I had the pleasure of talking to
(@garmelon). I believe it allowed to to suss
out what Waffle was spending the most time doing and why, specifically,
scrolling up and down felt so laggy and intensive on very long menus. I can’t
remember what I was doing before, but the solution turned out to be to instead
use BrickList
to display menus, basically.
Original content in gopherspace: gopher://gopher.someodd.zip:70/0/phlog/waffle.gopher.txt