• Waffle: Gopher Client
  • __ __ __ __ _ _
  • \ \ / / __ _ / _| / _| | | ___ (_)
  • \ \/\/ / / _` | | _| | _| | | / -_) _
  • \_/\_/ \__,_| |_| |_| |_| \___| (_)
  • ___ _
  • / __| ___ _ __ | |_ ___ _ _
  • | (_ | / _ \ | '_ \ | ' \ / -_) | '_|
  • \___| \___/ | .__/ |_||_| \___| |_|
  • |_|
  • ___ _ _ _
  • / __| | | (_) ___ _ _ | |_
  • | (__ | | | | / -_) | ' \ | _|
  • \___| |_| |_| \___| |_||_| \__|
  • ╔─*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──╗
  • ║1 .................................................. 1║
  • ║2* .................................................. *2║
  • ║3 .................................................. 3║
  • ║1 ................Posted: 2024-03-10................ 1║
  • ║2* Tags: nix gopher haskell debian showcase my_warez *2║
  • ║3 .................................................. 3║
  • ║1 .................................................. 1║
  • ╚──────────────────────────────────────────────────────────╝
  • I made a Gopher client called waffle[1]. 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[2])
  • * 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:
  • Waffle style configuration
  • Playing a hacking game in Waffle on mozz.us:
  • Playing a hacking game with Waffle
  • Waffle playing solitaire:
  • Waffle playing solitaire
  • Waffle playing Zork:
  • Waffle playing Zork
  • An example of Waffle's search and refresh abilities (also kind of showing off
  • caching):
  • Search and refresh in Waffle
  • ## 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:
  • ```
  • About Gopher
  • What's New!
  • Gopher Clients
  • Search Gopher Space
  • Gopher Protocol Specification
  • This is an information line
  • Fun & Games
  • University Information
  • Libraries around the World
  • Search a gopherhole
  • .
  • ```
  • 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[3]
  • 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 port `70`
  • 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[4].
  • 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`[5] module), which uses `Brick.BChan`[6], 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[7] the way I was
  • supposed to.
  • I used a terminal-based viewer called profold[8] for GHC `.prof` files generated
  • by profiling with `+RTS -p`, made by someone I had the pleasure of talking to
  • (@garmelon[9]). 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.
  • ## Footnotes
  • [1]: waffle: https://github.com/someodd/waffle
  • [2]: RFC 1436: https://www.rfc-editor.org/rfc/rfc1436
  • [3]: RFC1436, 3.8: Item Type Characters: https://www.rfc-editor.org/rfc/rfc1436
  • [4]: my gopher forum software: /showcase/gopherden
  • [5]: `Progress.hs`: https://github.com/someodd/waffle/blob/master/src/BrickApp/ModeAction/Progress.hs
  • [6]: `Brick.BChan`: https://hackage.haskell.org/package/brick-2.3.1/docs/Brick-BChan.html
  • [7]: brick: https://hackage.haskell.org/package/brick
  • [8]: profold: https://github.com/Garmelon/profold
  • [9]: @garmelon: https://github.com/Garmelon/