Package hg provides ☿ Mercury Protocol and Gemini Protocol client and server implementations, for the Go programming-language (golang).
The hg package provides an API in a style similar to the "net/http" package that is part of the Go standard library, including support for "middleware".
Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-hg
The ☿ Mercury Protocol is a simple client-server protocol.
The ☿ Mercury Protocol is derived from the Gemini Protocol — basically the Mercury Protocol is the Gemini Protocol without the TLS encryption. In a sense, the ☿ Mercury Protocol is a “naked” form of the Gemini Protocol.
The ☿ Mercury Protocol, through the Gemini Protocol, was inspired by the Gopher Protocol.
► To turn a ☿ Mercury Protocol server into a Gemini Protocol server,
launch the ☿ Mercury Protocol server on the address "localhost:1961" (rather than the usual ":1961"),
and then put a TLS proxy server in front of it (listening at ":1965") that modifies any "gemini://..." URI in the Gemini Protocol request into a "mercury://..." URI before sending it to the ☿ Mercury Protocol server.
(Or modify your handlers to accept both "mercury://..." and "gemini://..." URIs.)
A very simple ☿ Mercury Protocol server might look like this:
package main
import (
"github.com/reiver/go-hg"
"context"
"fmt"
"os"
)
func main() {
const address = ":1961"
var handler hg.Handler = hg.HandlerFunc(serveMercury)
err := hg.ListenAndServe(address, handler)
if nil != err {
fmt.Fprintln(os.Stderr, "problem with ☿ Mercury Protocol server:", err)
os.Exit(1)
return
}
}
func serveMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
w.WriteHeader(ctx, hg.StatusSuccess, "text/plain")
fmt.Fprintln(w.Writer(ctx), "Hello world!")
}In this example, the ☿ Mercury Protocol server just outputs a file with the contents “Hello world!”.
If you wanted to write your own ☿ Mercury Protocol server based on this code, then you would change what is inside the serveMercury() function.
A very simple ☿ Mercury Protocol client might look like this:
package main
import (
"github.com/reiver/go-hg"
"context"
"fmt"
"io"
"os"
)
func main() {
const address = "example.com:1961"
const uri = "mercury://example.com/apple/banana/cherry.txt"
var request hg.Request
err := request.Parse(uri)
if nil != err {
fmt.Fprintln(os.Stderr, "problem parsing URI:", err)
os.Exit(1)
return
}
responsereader, err := hg.DialAndCall(context.Background(), address, request)
if nil != err {
fmt.Fprintln(os.Stderr, "problem with request:", err)
os.Exit(1)
return
}
defer responsereader.Close()
var ctx context.Context = context.Background()
io.Copy(os.Stdout, responsereader.Reader(ctx))
}In this code, the download file is just outputted to STDOUT. You could modify this code to do whatever you want.
Note that we can do more sophisticated things by inspecting the error that was returned. To deal with redirects, etc.
So, we could do that with code like the following:
package main
import (
"github.com/reiver/go-hg"
"context"
"fmt"
"io"
"os"
)
func main() {
const address = "example.com:1961"
const uri = "mercury://example.com/apple/banana/cherry.gmni"
var request hg.Request
err := request.Parse(uri)
if nil != err {
fmt.Fprintf(os.Stderr, "problem parsing URI %q: %s\n", uri, err)
os.Exit(1)
return
}
ctx := context.Background()
responsereader, err := hg.DialAndCall(ctx, address, request)
if nil != err {
fmt.Fprintf(os.Stderr, "problem dialing and connecting to %q: %s", uri, err)
os.Exit(1)
return
}
defer responsereader.Close()
_, err = io.Copy(os.Stdout, responsereader.Reader(context.Background()))
if nil != err {
fmt.Fprintf(os.Stderr, "problem outputing response body for %q to STDOUT: %s", uri, err)
os.Exit(1)
return
}
}The ☿ Mercury Protocol and the Gemini Protocol are often used with a (specific) hypermedia & hypertext file data format known as gemtext.
(The name “gemtext” is short for “gemini text”.)
Gemtext is a formatted text file data format similar to markdown, and inspired by the line typing convention in Gopher.
Here is an example gemtext file:
# Joe Blow's Capsule
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nibh cras pulvinar mattis nunc sed blandit libero volutpat. Tellus mauris a diam maecenas. Quis enim lobortis scelerisque fermentum dui faucibus. Sed id semper risus in hendrerit gravida rutrum quisque non. Pretium vulputate sapien nec sagittis. Ut aliquam purus sit amet luctus venenatis lectus magna fringilla. Scelerisque eleifend donec pretium vulputate sapien. A lacus vestibulum sed arcu non odio. Lacus luctus accumsan tortor posuere ac. Vestibulum lectus mauris ultrices eros in cursus. Id nibh tortor id aliquet lectus proin nibh nisl. Fermentum et sollicitudin ac orci. Id faucibus nisl tincidunt eget nullam non nisi. Mi quis hendrerit dolor magna eget est lorem ipsum dolor. Hendrerit gravida rutrum quisque non tellus orci ac auctor augue. Ut enim blandit volutpat maecenas. Arcu dui vivamus arcu felis.
Eget aliquet nibh praesent tristique magna sit amet. Mi bibendum neque egestas congue quisque egestas diam in. Massa eget egestas purus viverra accumsan in nisl nisi. Ultricies integer quis auctor elit sed vulputate. Sed odio morbi quis commodo odio aenean sed. Sed sed risus pretium quam vulputate. Feugiat in fermentum posuere urna. Tincidunt praesent semper feugiat nibh sed. Non sodales neque sodales ut etiam. Sapien eget mi proin sed libero enim. Vel facilisis volutpat est velit egestas. Purus viverra accumsan in nisl nisi scelerisque. Laoreet sit amet cursus sit amet dictum. Sollicitudin ac orci phasellus egestas tellus rutrum.
=> mercury://example.com/once/twice/thrice/fource.txt Tortor aliquam nulla facilisi cras.
Some of the built-in handlers in this package will output gemtext.
One can turn a ☿ Mercury Protocol server into a Gemini Protocol server by, very roughly, putting a TLS layer over top of it (and dealing the 6x response status codes).
If one wants to have a Gemini Protocol server, but handle the TLS encryption at another level from server, then (using this package and) setting up a Mercury Protocol server can enable that.
A very very simple ☿ Mercury Protocol server is shown in the following code.
This particular ☿ Mercury Protocol server just responds to the client with the URI that was in the request, plus the remote address.
package main
import (
"github.com/reiver/go-hg"
)
func main() {
var handler hg.Handler = hg.DebugHandler
err := hg.ListenAndServe(":1961", handler)
if nil != err {
//@TODO: Handle this error better.
panic(err)
}
}Another example ☿ Mercury Protocol server is shown in the following code:
package main
import (
"github.com/reiver/go-hg"
)
func main() {
var handler hg.Handler = &hg.UserDirHandler{}
err := hg.ListenAndServe(":1961", handler)
if nil != err {
//@TODO: Handle this error better.
panic(err)
}
}Here the handler — hg.UserDirHandler — operates similar to Apache's HTTP Server Project's mod_userdir — in that it enables user-specific directories such as /home/username/mercury_public/ to be accessed over the Mercury Protocol using the tilde path mercury://example.com/~username/
And finally, here is a custom handler being used in a ☿ Mercury Protocol server:
package main
import (
"github.com/reiver/go-hg"
"context"
"io"
)
func main() {
var handler hg.Handler = myCustomHandler{}
err := hg.ListenAndServe(":1961", handler)
if nil != err {
//@TODO: Handle this error better.
panic(err)
}
}
type myCustomHandler struct{}
func (receiver myCustomHandler) ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
io.WriteString(w.Writer(ctx), "Hello world!")
}Alternatively, this could be made a bit simpler if hg.HandlerFunc() is used:
package main
import (
"github.com/reiver/go-hg"
"context"
"io"
)
func main() {
var handler hg.Handler = hg.HandlerFunc(helloworld)
err := hg.ListenAndServe(":1961", handler)
if nil != err {
//@TODO: Handle this error better.
panic(err)
}
}
func helloworld(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
io.WriteString(w.Writer(ctx), "Hello world!")
}This package provides a number of helper-functions that make responding to a ☿ Mercury Protocol request easier. The helper functions are:
| Mercury Protocol Response | Basic Usage | Intermediate Usage |
|---|---|---|
10 INPUT |
hg.ServeInput(ctx, w, prompt...) |
|
11 SENSITIVE INPUT |
hg.ServeSensitiveInput(ctx, w, prompt...) |
|
20 SUCCESS |
||
30 REDIRECT - TEMPORARY |
hg.ServeRedirectTemporary(ctx, w, url) |
|
31 REDIRECT - PERMANENT |
hg.ServeRedirectPermanent(ctx, w, url) |
|
40 TEMPORARY FAILURE |
hg.ServeTemporaryFailure(ctx, w) |
hg.ServeTemporaryFailure(ctx, w, info) |
41 SERVER UNAVAILABLE |
hg.ServeServerUnavailable(ctx, w) |
hg.ServeServerUnavailable(ctx, w, info) |
42 CGI ERROR |
hg.ServeCGIError(ctx, w) |
hg.ServeCGIError(ctx, w, info) |
43 PROXY ERROR |
hg.ServeProxyError(ctx, w) |
hg.ServeProxyError(ctx, w, info) |
44 SLOW DOWN |
hg.ServeSlowDown(ctx, w, retryAfter) |
|
50 PERMANENT FAILURE |
hg.ServePermanentFailure(ctx, w) |
hg.ServePermanentFailure(ctx, w, info) |
51 NOT FOUND |
hg.ServeNotFound(ctx, w) |
hg.ServeNotFound(ctx, w, info) |
52 GONE |
hg.ServeGone(ctx, w) |
hg.ServeGone(ctx, w, info) |
53 PROXY REQUEST REFUSED |
hg.ServeProxyRequestRefused(ctx, w) |
hg.ServeProxyRequestRefused(ctx, w, info) |
59 BAD REQUEST |
hg.ServeBadRequest(ctx, w) |
hg.ServeBadRequest(ctx, w, info) |
To import package hg use import code like the following:
import "github.com/reiver/go-hg"
To install package hg do the following:
GOPROXY=direct go get github.com/reiver/go-hg
Package hg was written by Charles Iliya Krempeaux
The package name of this Go package is hg rather than mercury because Hg is often used as a shorthand for mercury.
Nowadays the word mercury is used to refer to multiple things — a Roman god named “Mercury”, a chemical element named “mercury”, a planet named “mercury”, a space-mission named “Project Mercury”, and now also a network protocol named the “Mercury Protocol”.
The relationship between these different things named “mercury” is as follows —
The Mercury Protocol was named after the Project Mercury space-mission.
The Project Mercury space-mission was named after the Roman god named Mercury. The Project Mercury space-mission also used a modified version astrological-symbol for the planet mercury (☿) for its logo.
The chemical-element mercury was also named after Roman god named Mercury.
An older name for the chemical-element mercury is hydrargyrum.
“Hydrargyrum” is a romanized version of the ancient Greek word “ὑδράργυρος” (hydrargyros). The ancient Greek word “ὑδράργυρος” (hydrargyros) is a compound word: “ὑδρ” + “άργυρος”. The first part ὑδρ- (hydr-) comes from the root ὕδωρ water (although in this context it might be more accurate to interpret it as liquid rather than water). The second part ἄργυρος (argyros) means silver (although in this context it might be more accurate to interpret it as shiny rather than silver). So ὑδράργυρος” (hydrargyros) is water-silver, although perhaps more accurately interpretted as liquid-shiny
“Hg” is the chemical-symbol for the chemical-element mercury because “Hg” is short for “hydrargyrum”.
And thus this, a package that implements the Mercury Protocol, is named hg.
██╗░░██╗██╗░░░██╗██████╗░██████╗░░█████╗░██████╗░░██████╗░██╗░░░██╗██████╗░██╗░░░██╗███╗░░░███╗
██║░░██║╚██╗░██╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝░╚██╗░██╔╝██╔══██╗██║░░░██║████╗░████║
███████║░╚████╔╝░██║░░██║██████╔╝███████║██████╔╝██║░░██╗░░╚████╔╝░██████╔╝██║░░░██║██╔████╔██║
██╔══██║░░╚██╔╝░░██║░░██║██╔══██╗██╔══██║██╔══██╗██║░░╚██╗░░╚██╔╝░░██╔══██╗██║░░░██║██║╚██╔╝██║
██║░░██║░░░██║░░░██████╔╝██║░░██║██║░░██║██║░░██║╚██████╔╝░░░██║░░░██║░░██║╚██████╔╝██║░╚═╝░██║
╚═╝░░╚═╝░░░╚═╝░░░╚═════╝░╚═╝░░╚═╝╚═╝░░╚═╝╚═╝░░╚═╝░╚═════╝░░░░╚═╝░░░╚═╝░░╚═╝░╚═════╝░╚═╝░░░░░╚═╝
- The Mercury protocol (gemini)
- The Mercury protocol (http proxy)
- Mailing List thread: “Mercury”