Documentation
¶
Index ¶
- Constants
- func ErrorResponse(statuscode int, meta string) error
- func GenerateClientCertificate() (*tls.Certificate, error)
- func ListenAndServe(addr string, handler Handler) error
- func Serve(listener net.Listener, handler Handler) error
- func ServeBadRequest(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeCGIError(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeGone(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeInput(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeNotFound(ctx context.Context, w ResponseWriter, a ...any) error
- func ServePermanentFailure(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeProxyError(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeProxyRequestRefused(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeRedirectPermanent(ctx context.Context, w ResponseWriter, target string) error
- func ServeRedirectTemporary(ctx context.Context, w ResponseWriter, target string) error
- func ServeSensitiveInput(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeServerUnavailable(ctx context.Context, w ResponseWriter, a ...any) error
- func ServeSlowDown(ctx context.Context, w ResponseWriter, numberOfSecondsToWait uint) error
- func ServeTemporaryFailure(ctx context.Context, w ResponseWriter, a ...any) error
- func StatusText(code int) string
- type Field
- type FileSystemHandler
- type Handler
- type HandlerFunc
- type Logger
- type Request
- func (receiver Request) IsNothing() bool
- func (receiver Request) MarshalText() ([]byte, error)
- func (receiver *Request) Parse(src any) error
- func (receiver Request) RequestValue() string
- func (receiver Request) Scheme() string
- func (receiver Request) String() string
- func (receiver Request) TCPAddr() (string, bool)
- func (receiver *Request) UnmarshalText(text []byte) error
- func (receiver Request) WriteTo(w io.Writer) (int64, error)
- type ResponseBadRequest
- type ResponseCGIError
- type ResponseCertificateNotAuthorized
- type ResponseCertificateNotValid
- type ResponseCertificateRequired
- type ResponseGone
- type ResponseInput
- type ResponseNotFound
- type ResponsePermanentFailure
- type ResponseProxyError
- type ResponseProxyRequestRefused
- type ResponseReader
- func Call(ctx context.Context, conn net.Conn, request Request) (ResponseReader, error)
- func DialAndCall(ctx context.Context, addr string, request Request) (ResponseReader, error)
- func DialAndCallTLS(ctx context.Context, addr string, request Request, tlsHandler TLSHandler) (ResponseReader, error)
- func DialAndCallURL(ctx context.Context, url string, tlsHandler TLSHandler) (ResponseReader, error)
- type ResponseRedirectPermanent
- type ResponseRedirectTemporary
- type ResponseSensitiveInput
- type ResponseServerUnavailable
- type ResponseSlowDown
- type ResponseTemporaryFailure
- type ResponseWriter
- type Server
- type TLSConfig
- type TLSHandler
- type TimeoutHandler
- type UnknownResponse
- type UserDirHandler
Constants ¶
const ( DefaultMetaInput = StatusTextInput // 10 DefaultMetaSensitiveInput = StatusTextSensitiveInput // 11 DefaultMetaSuccess = StatusTextSuccess // 20 DefaultMetaTemporaryFailure = StatusTextTemporaryFailure // 40 DefaultMetaCGIError = StatusTextCGIError // 42 DefaultMetaProxyError = StatusTextProxyError // 43 DefaultMetaSlowDown = "3" // 44 DefaultMetaPermanentFailure = StatusTextPermanentFailure // 50 DefaultMetaNotFound = StatusTextNotFound // 51 DefaultMetaGone = StatusTextGone // 52 DefaultMetaProxyRequestRefused = StatusTextProxyRequestRefused // 53 DefaultMetaBadRequest = StatusTextBadRequest // 59 )
These are constants that can be used as default values for a Mercury Protocol's response‐header's meta.
For example usage:
hg.ServeNotFound(ctx, w, hg.DefaultMetaNotFound)
Also for another example usage:
hg.ServeTemporaryFailure(ctx, w, hg.DefaultMetaTemporaryFailure)
To understand these —
The Mercury Protocol is based on Gemini Protocol. And therefore a Mercury Protocol response‐header's structure is defined in the Gemini Protocol's specification. In the Gemini Protocol specification, the response‐header is described as follows:
<STATUS><SPACE><META><CR><LF>
In Go code, this (the Mercury Protocol's response‐header) is equivalent to:
twoDigitStatusNumericalCode + " " + meta + "\r\n"
For most Mercury Protocol response types, the value of the response‐header's ‘meta’ is likely cosmetic. And possibly, no human will ever see them (depending on whether the client software presents them to the user or not). The following constants provide useful default values for these cosmetic meta value's, that can make a programmer's life easier when developing a Mercury Protocol client or server:
- DefaultMetaSuccess = "success" // 20
- DefaultMetaTemporaryFailure = "temporary-failure" // 40
- DefaultMetaServerUnavailable = "server-unavailable" // 41
- DefaultMetaCGIError = "cgi-error" // 42
- DefaultMetaProxyError = "proxy-error" // 43
- DefaultMetaPermanentFailure = "permanent-failure" // 50
- DefaultMetaNotFound = "not-found" // 51
- DefaultMetaGone = "gone" // 52
- DefaultMetaProxyRequestRefused = "proxy-request-refused" // 53
- DefaultMetaBadRequest = "bad-request" // 59
Two of these default response‐header’s ‘meta’ are (not cosmetic but are) shown to the user. The programmer SHOULD create their own message; but just in case they don't, these default values exist:
• DefaultMetaInput = "input" // 10
• DefaultMetaSensitiveInput = "sensitive-input" // 11
In addition to these, one of these default response‐header is functional.
DefaultMetaSlowDown = "3" // 44
For status 44, the meta is the number of seconds the client MUST wait before making another request. So "3" means "wait 3 seconds before retrying". This SHOULD be chosen by the programmer; but just in case they don't, a default value exists.
const ( // A Mercury Protocol server runs over TCP. // TCP has communications happening over TCP-ports. // A client-server protocol (including the Mercury Protocol) typically defines a default-TCP-port for servers. // For the Mercury Protocol, this default-TCP-port is: 1961. // // This constant — ‘DefaultTCPPort’ — can be used when one wants to use the default-TCP-port for a Mercury Protocol server. // // For example: // // var domain string = "example.com" // // var address string = fmt.Sprintf("%s:%d", domain, hg.DefaultTCPPort) // // err := hg.ListenAndServe(address, handler) DefaultTCPPort = 1961 DefaultTCPPortString = "1961" // A Gemini Protocol server runs over TLS over TCP. // TCP has communications happening over TCP-ports. // A client-server protocol (including the Gemini Protocol) typically defines a default-TCP-port for servers. // For the Gemini Protocol, this default-TCP-port is: 1965. // // This constant — ‘DefaultTCPPortTLS’ — can be used when one wants to use the default-TCP-port for a Gemini Protocol server. // // For example: // // var domain string = "example.com" // // var address string = fmt.Sprintf("%s:%d", domain, hg.DefaultTCPPortTLS) // // err := hg.ListenAndServe(address, handler) DefaultTCPPortTLS = 1965 DefaultTCPPortTLSString = "1965" )
const ( ErrBadStatusCode = erorr.Error("bad status code") ErrBadResponseHeaderMeta = erorr.Error("bad response header meta") ErrBadTCPAddr = erorr.Error("bad TCP address") ErrCannotParse = erorr.Error("cannot parse") ErrContextDone = erorr.Error("context done") ErrDialError = erorr.Error("dial error") ErrNilNetworkConnection = erorr.Error("nil network connection") ErrNilReceiver = erorr.Error("nil receiver") ErrNilResponseReader = erorr.Error("nil response reader") ErrNilResponseWriter = erorr.Error("nil response writer") ErrRequestIsNothing = erorr.Error("request is nothing") ErrResponseHeaderMetaTooBig = erorr.Error("response header meta too big") ErrSchemeUnsupported = erorr.Error("scheme unsupported") ErrServerCertificateNotFound = erorr.Error("server did not present any certificate(s)") ErrServerShutdown = erorr.Error("server shutdown") ErrTargetTypeUnsupported = erorr.Error("target type unsupported") ErrWriteError = erorr.Error("write error") )
const ( Scheme = "mercury" SchemeTLS = "gemini" )
const ( StatusInput = 10 StatusSensitiveInput = 11 StatusSuccess = 20 StatusRedirectTemporary = 30 StatusRedirectPermanent = 31 StatusTemporaryFailure = 40 StatusCGIError = 42 StatusProxyError = 43 StatusSlowDown = 44 StatusPermanentFailure = 50 StatusNotFound = 51 StatusGone = 52 StatusProxyRequestRefused = 53 StatusBadRequest = 59 // Gemini Protocol only. // Not used in the Mercury Protocol. StatusCertificateRequired = 60 StatusCertificateNotAuthorized = 61 StatusCertificateNotValid = 62 )
Constants for the Mercury Protocol status codes.
Can use, for example, with ResponseWriter's WriteHeader method.
For example:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
w.WriteHeader(ctx, hg.StatusNotFound, "uh oh!")
}
const ( StatusTextInput = "input" // 10 StatusTextSensitiveInput = "sensitive-input" // 11 StatusTextSuccess = "success" // 20 StatusTextRedirectTemporary = "temporary-redirection" // 30 StatusTextRedirectPermanent = "permanent-redirection" // 31 StatusTextTemporaryFailure = "temporary-failure" // 40 StatusTextCGIError = "cgi-error" // 42 StatusTextProxyError = "proxy-error" // 43 StatusTextSlowDown = "slow-down" // 44 StatusTextPermanentFailure = "permanent-failure" // 50 StatusTextNotFound = "not-found" // 51 StatusTextGone = "gone" // 52 StatusTextProxyRequestRefused = "proxy-request-refused" // 53 StatusTextBadRequest = "bad-request" // 59 // Gemini Protocol only. // Not used in the Mercury Protocol. StatusTextCertificateRequired = "certificate-required" // 60 StatusTextCertificateNotAuthorized = "certificate-not-authorized" // 61 StatusTextCertificateNotValid = "certificate-not-valid" // 62 )
const DebugHandler internalDebugHandler = internalDebugHandler(0)
The DebugHandler can be used as:
- a demo Mercury Protocol server,
- a debugging tool to use with Mercury Protocol clients.
You can use it with code similar to:
const address = ":1961" err := hg.ListenAndServe(address, hg.DebugHandler)
Variables ¶
This section is empty.
Functions ¶
func ErrorResponse ¶
ErrorResponse returns the appropriate response type for the given status-code & meta.
And note that any Mercury Protocol response type other that “20 SUCCESS” is considered an error.
Example With Bad Request ¶
So, for example, this:
hg.ErrorResponse(59, "")
Would return:
hg.ResponseBadRequest{meta:""}
Example With Temporary Failure ¶
And, for example, this:
hg.ErrorResponse(40, "we seem to be experiencing some technical difficulties")
Would return:
hg.ResponseTemporaryFailure{meta:"we seem to be experiencing some technical difficulties"}
Example With Success ¶
Although note that calling with a status-code of 20 (i.e., the status code for Success) would return nil. So, for example, this:
hg.ErrorResponse(20, "text/gemini")
Would return
nil
Type Switch ¶
This is useful with type switches. For example:
func callMercury(rr hg.ResponseReader, r hg.Request) {
// ...
p, err := io.ReadAll(rr.Reader(ctx))
if nil != err {
switch casted := err.(type) {
case hg.ResponseInput:
//@TODO
case hg.ResponseSensitiveInput:
//@TODO
case hg.ResponseRedirectTemporary:
//@TODO
case hg.ResponseRedirectPermanent:
//@TODO
case hg.ResponseTemporaryFailure:
//@TODO
case hg.ResponseServerUnavailable:
//@TODO
case hg.ResponseCGIError:
//@TODO
case hg.ResponseProxyError:
//@TODO
case hg.ResponseSlowDown:
//@TODO
case hg.ResponsePermanentFailure:
//@TODO
case hg.ResponseNotFound :
//@TODO
case hg.ResponseGone:
//@TODO
case hg.ResponseProxyRequestRefused:
//@TODO
case hg.ResponseBadRequest:
//@TODO
case hg.ResponseCertificateRequired:
//@TODO
case hg.ResponseCertificateNotAuthorized:
//@TODO
case hg.ResponseCertificateNotValid:
//@TODO
case hg.UnknownResponse:
//@TODO
default:
//@TODO
}
// ...
}
func GenerateClientCertificate ¶
func GenerateClientCertificate() (*tls.Certificate, error)
GenerateClientCertificate generates a self-signed TLS client certificate and returns it directly as a *tls.Certificate (including private-key).
This is useful when speaking protocols that use TLS client certificates for identity (such as the Gemini Protocol). In the Gemini Protocol, a server may respond with a "60 CLIENT CERTIFICATE REQUIRED" status code, indicating the client must present a certificate. Gemini clients routinely generate throwaway or per-site self-signed certificates on the fly when this happens.
Example usage:
rr, err := hg.DialAndCallTLS(ctx, addr, request, nil)
// ... get back ResponseCertificateRequired ...
cert, err := hg.GenerateClientCertificate()
rr, err = hg.DialAndCallTLS(ctx, addr, request, hg.TLSConfig{
ClientCertificateFunc: func(host string) *tls.Certificate {
return cert
},
})
See also:
func ListenAndServe ¶
ListenAndServe listens on the TCP network address `addr` and then spawns a call to the ServeMercury method on the `handler` to serve each incoming connection.
For a very simple example:
package main
import (
"github.com/reiver/go-hg"
)
func main() {
//@TODO: In your code, you would probably want to use a different handler.
var handler hg.Handler = hg.DebugHandler
err := hg.ListenAndServe(":1961", handler)
if nil != err {
//@TODO: Handle this error better.
panic(err)
}
}
func Serve ¶
Serve accepts an incoming Mercury Protocol client connection on the net.Listener `listener`.
For a very simple example:
package main
import (
"github.com/reiver/go-hg"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":1961")
if nil != err {
//@TODO: Handle this error better.
panic(err)
}
//@TODO: In your code, you would probably want to use a different handler.
var handler hg.Handler = hg.DebugHandler
err = hg.Serve(listener, handler)
if nil != err {
//@TODO: Handle this error better.
panic(err)
}
}
func ServeBadRequest ¶
func ServeBadRequest(ctx context.Context, w ResponseWriter, a ...any) error
59 BAD REQUEST
This function sends a “59 BAD REQUEST” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var message string = "you did not enter a number"
hg.ServeBadRequest(ctx, w, message)
// ...
}
func ServeCGIError ¶
func ServeCGIError(ctx context.Context, w ResponseWriter, a ...any) error
42 CGI ERROR
This function sends a “42 CGI ERROR” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var message string = "the program being run just had an unexpected fatal error"
hg.ServeCGIError(ctx, w, message)
// ...
}
func ServeGone ¶
func ServeGone(ctx context.Context, w ResponseWriter, a ...any) error
52 GONE
This function sends a “52 GONE” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var message string = "he's dead jim"
hg.ServeGone(ctx, w, message)
// ...
}
func ServeInput ¶
func ServeInput(ctx context.Context, w ResponseWriter, a ...any) error
10 INPUT
This function sends a “10 INPUT” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var prompt string = "Pick a number between 1 and 10"
hg.ServeInput(ctx, w, prompt)
// ...
}
func ServeNotFound ¶
func ServeNotFound(ctx context.Context, w ResponseWriter, a ...any) error
51 NOT FOUND
This function sends a “51 NOT FOUND” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var message string = "this is not the gem-page you are looking for"
hg.ServeNotFound(ctx, w, message)
// ...
}
func ServePermanentFailure ¶
func ServePermanentFailure(ctx context.Context, w ResponseWriter, a ...any) error
50 PERMANENT FAILURE
This function sends a “50 PERMANENT FAILURE” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var message string = "someone deleted the database"
hg.ServePermanentFailure(ctx, w, message)
// ...
}
func ServeProxyError ¶
func ServeProxyError(ctx context.Context, w ResponseWriter, a ...any) error
43 PROXY ERROR
This function sends a “43 PROXY ERROR” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var message string = "the proxy server providing TLS encryption errored out"
hg.ServeProxyError(ctx, w, message)
// ...
}
func ServeProxyRequestRefused ¶
func ServeProxyRequestRefused(ctx context.Context, w ResponseWriter, a ...any) error
53 PROXY REQUEST REFUSED
This function sends a “53 PROXY REQUEST REFUSED” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var message string = "you did not enter a number"
hg.ServeProxyRequestRefused(ctx, w, message)
// ...
}
func ServeRedirectPermanent ¶
func ServeRedirectPermanent(ctx context.Context, w ResponseWriter, target string) error
31 REDIRECT - PERMANENT
This function sends a “31 REDIRECT - PERMANENT” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
//var url string = "/apple/banana/cherry.txt"
//var url string = "documents/info.txt"
var url string = "mercury://example.com/once/twice/thrice/fource.txt"
hg.ServeRedirectPermanent(ctx, w, url)
// ...
}
func ServeRedirectTemporary ¶
func ServeRedirectTemporary(ctx context.Context, w ResponseWriter, target string) error
30 REDIRECT - TEMPORARY
This function sends a “30 REDIRECT - TEMPORARY” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
//var url string = "/apple/banana/cherry.txt"
//var url string = "documents/info.txt"
var url string = "mercury://example.com/once/twice/thrice/fource.txt"
hg.ServeRedirectTemporary(ctx, w, url)
// ...
}
func ServeSensitiveInput ¶
func ServeSensitiveInput(ctx context.Context, w ResponseWriter, a ...any) error
11 SENSITIVE INPUT
This function sends a “11 SENSITIVE INPUT” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var prompt string = "Please enter your password"
hg.ServeSensitiveInput(ctx, w, prompt)
// ...
}
func ServeServerUnavailable ¶
func ServeServerUnavailable(ctx context.Context, w ResponseWriter, a ...any) error
41 SERVER UNAVAILABLE
This function sends a “41 SERVER UNAVAILABLE” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var message string = "we are upgrading the server"
hg.ServeServerUnavailable(ctx, w, message)
// ...
}
func ServeSlowDown ¶
func ServeSlowDown(ctx context.Context, w ResponseWriter, numberOfSecondsToWait uint) error
44 SLOW DOWN
This function sends a “44 SLOW DOWN” Mercury Protocol response.
Example Usage ¶
This is how one might use this helper-function:
func ServeMercury(ctx context.Context, w hg.ResponseWriter, r hg.Request) {
// ...
var numberOfSecondsToWait uint = 8
hg.ServeSlowDown(ctx, w, numberOfSecondsToWait)
// ...
}