-
- 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/