Protocol Junkie: Gopher

protocol-junkie protocol gopher

Gopher is a communication protocol designed for distributing, searching, and retrieving documents in Internet Protocol networks that was invented by Mark P. McCahill at the University of Minnesota in 1991.

People that think that the web is bloated, often bring up gopher when talking about ways of fixing the internet. Their way of fixing the bloatness of the web is by limiting what you can do with it; and I’m all for a bloatless web, but not by limiting what people can do with it.

One more thing to think about: You wouldn’t even hear about the people that are actually advocating for this, because they would all be hidden in their so called “gopher holes”.

One thing that I must admit, is the fact that gopher is suitable for large online encyclopedias or other systems that store lots of information. I don’t think that there currently is something more suitable for such a task. Gopher’s hierarhical way of serving data is just perfect for that kind of stuff.

The protocol

The gopher protocol is so simple, the server doesn’t even need to handle any kinda of state; it almost feels wrong calling it a protocol. Imagine that: a small, efficient and state-less protocol that can deliever the same kind of content as a 20 MB news page.

In essence, the Gopher protocol consists of a client connecting to a server and sending the server a selector (a line of text, which may be empty) via a TCP connection. The server responds with a block of text terminated with a period on a line by itself, and closes the connection. No state is retained by the server between transactions with a client. The simple nature of the protocol stems from the need to implement servers and clients for the slow, smaller desktop computers (1 MB Macs and DOS machines), quickly, and efficiently.

I think that quote summarizes almost everything. I’m just gonna list some code snippets that implement what it says.

package main

import (
    "fmt"
    "io/ioutil"
    "net"
    "strings"
    "time"
)

var (
    crlf = "\r\n"
    timeout = 15 * time.Second
)

func Retrieve(host, port, resource string) ([]byte, error) {
        nullResp := make([]byte, 0)
        addr := host + ":" + port

        // Opens TCP connection
       conn, err := net.DialTimeout("tcp", addr, timeout)
        if err != nil {
                return nullResp, err
        }

        // Sends an empty line: Meaning "list what you have")
       _, err = conn.Write([]byte(resource + crlf))
        if err != nil {
                return nullResp, err
        }

        // Read server's response
       result, err := ioutil.ReadAll(conn)
        if err != nil {
                return nullResp, err
        }

        return result, err
}

func main() {
    res, _ := Retrieve("gopher.floodmap.com", "70", "/")
    fmt.Println(res)
}

The RFC also shows this nice example interaction between a client and a server.

Client: {Opens connection to rawBits.micro.umn.edu at port 70}

Server: {Accepts connection but says nothing}

Client: <CR><LF> {Sends an empty line: Meaning "list what you have"}

Server: {Sends a series of lines, each ending with CR LF}
    0About internet GopherFStuff:About usFrawBits.micro.umn.eduF70
    1Around University of MinnesotaFZ,5692,AUMFunderdog.micro.umn.eduF70
    1Microcomputer News & PricesFPrices/Fpserver.bookstore.umn.eduF70
    1Courses, Schedules, CalendarsFFevents.ais.umn.eduF9120
    1Student-Staff DirectoriesFFuinfo.ais.umn.eduF70
    1Departmental PublicationsFStuff:DP:FrawBits.micro.umn.eduF70
    {.....etc.....}
    . {Period on a line by itself}
{Server closes connection}

Parsing the response

Because the response is plain text, we need to loop over each line. The line’s first character represents the item type and the rest of the line represents it’s value + some tab-separated “arguments”.

Char Type
0 Text file
1 Gopher submenu
2 CCSO Nameserver
3 Error code returned by a Gopher server to indicate failure
4 BinHex-encoded file (primarily for Macintosh computers)
5 DOS file
6 uuencoded file
7 Gopher full-text search
8 Telnet
9 Binary file
+ Mirror or alternate server
g GIF file
I Image file
T Telnet 3270

Char Type
d Doc. Seen used alongside PDF’s and .DOC’s
h HTML file
i Informational message, widely used.[24]
j image file “(especially the jpg format)”
p image file “(especially the png format)”
r document rtf file “rich text Format”)
s Sound file (especially the WAV format)
w document wri file “write of microsoft Format”)
P document pdf file “Portable Document Format”)
X document xml file “eXtensive Markup Language” )
package main

// func Recieve(...) ... {
//    ...
// }

func main() {
    res, _ := Retrieve("gopher.floodmap.com", "70", "/")
    lines := string.Split(string(res), crlf)

    for _, l := range lines {
        if l[0] == '0' {
            // file
        } else if l[0] == '1' {
            // directory
        } // and so on ...
    }
}

Search transactions

The search transaction allows you to search through the content that a server provides, but because gopher is so simple and extensible, I can really see other ways to use this.

C: Opens Connection.
C: Sends Selector String, Tab, Search String.
S: Sends Menu Entity.

Conclusion

At first, I considered gopher an old, outdates and limited protocol that has no use in the modern internet; and part of that might be true, but I can really see this protocol being useful for wikis or encyclopedias.
I still don’t think that gopher is the solution to the bloatness of the web, but I must admit that gopher is really extensible and it could be put to work in some parts of the internet.

Read the RFC (or the PDF)