Documentation
¶
Index ¶
Examples ¶
Constants ¶
const ( Text = "" Link = "=> " Pre = "```" // before and after preformatted text Header = "# " Header2 = "## " Header3 = "### " // three levels of header are defined in the specification v0.16.1 UL = "* " // unordered list Quote = "> " )
Prefixes defined in the Gemini specification.
const DefaultAddress = ":1965"
Defined in Gemini specification
const ProxyRefused = 53
Variables ¶
var ErrParse = errors.New("Parsing error")
var ErrResponseTooLarge = errors.New("Payload larger than defined ReadSize")
Passed if response is larger than Client.ReadSize
var ErrTimeout = errors.New("gemini client timeout")
var ErrWrongProtocol = errors.New("Unsupported protocol")
Functions ¶
func InsertCarriageReturn ¶ added in v0.4.0
Insert carriage-returns (\r) in front of newline characters (\n). Using carriage-returns along with newlines is required for the header line and not for the rest of the document.
InsertCarriageReturn does not interfere with any newlines that are alreay preceeded by a carriage-return.
Types ¶
type Client ¶
type Client struct { Dialer // Dialer may include timeout (see net.Dialer) ReadSize int64 // Gemini protocol does not broadcast Content-Size. Reject responses larger than this. ReadTimeout time.Duration // After sending request, connection will timeout after this amount of time. Logger io.Writer // If set (!=nil), log requests to this writer. // contains filtered or unexported fields }
Gemini client. Uses Dialer. Includes maximum read size to block "gembomb" attacks or too large payloads and ReadTimeout to prevent hangs.
Example ¶
d := Client{ Dialer: &net.Dialer{Timeout: time.Second}, ReadSize: 1024 * 1024 * 16, // 16mB ReadTimeout: time.Second, } u, err := url.Parse("gemini://gemini.circumlunar.space/") if err != nil { fmt.Println(err.Error()) return } if _, err := d.Get(u); err != nil { fmt.Println(err.Error()) return }
Output:
func (Client) Get ¶
Make a request to a gemini capsule. To prevent hangs on timeout, use net.Dialer and set Timeout and ReadTimeout. If the hostname is not set in the url, substitute post number 1965. If no protocol is supplied, substitute gemini:// Will pass any errors recieved along the way, or ErrTimeout or ErrResponseTooLarge.
type Dialer ¶
Interface used by Dial. golang.org/x/net/proxy.Dialer and net.Dialer are compatible with this.
type Handler ¶
Handler function that is called by gemini.Server for each incoming TCP connection. *url.URL is the parsed URL from the request, from which the path, query value (for input), or hostname (for reverse proxies) can be found. *tls.Conn contains information about the client including their IP address and certificates if any. The gemini.Response is handled by gemini.Server and returned to the client.
type Lines ¶
type Lines []string
Slice of UTF-8 encoded lines to be printed by the capsule (not including status and mime type). Note that each string includes the prompt (you may choose to use constants by for example `fmt.Sprintf("%header text", gemini.Header)`. Each line MUST not include any whitespace at the prefix or suffix, including newlines (note that the Gemini protocol specifies Windows-style newlines including carriage returns).
Lines is NOT thread safe. (The capsule should not write to this lines struct asyncronously, due to ambiguity due to race conditions).
Note that Lines uses strings which are UTF-8 encoded. To serve content in other encodings, see ResponsePlain
Example ¶
var l Lines l.Line("1") l.Line("2", "3") l.Link("example.net") l.LinkDesc("example.net", "a website") l.Pre("Hello.", "This is a pre-formatted block.", "Goodbye.") l.Header(0, "header") l.Header(1, "header") l.Header(2, "header") l.Header(3, "header") l.Header(4, "header") l.UL("first", "second", "third") l.Quote("Hello.\nThis is a quote.\r\n Goodbye!") response := ResponseFormat{Status: Success, Mime: "text/gemini", Lines: l} fmt.Printf("%s", bytes.ReplaceAll(response.Bytes(), []byte("\r\n"), []byte("\n")))
Output: 20 text/gemini 1 2 3 => example.net => example.net a website ``` Hello. This is a pre-formatted block. Goodbye. ``` # header # header ## header ### header # header * first * second * third > Hello. > This is a quote. > Goodbye!
func (*Lines) Header ¶
Add header line. Main header, level = 0 or 1. 2 = second layer header, 3 = third layer header. If any other integer, default to highest-level header.
func (*Lines) Line ¶
Append line. Accepts multiple lines as string arguments, to be added after each other
func (*Lines) Pre ¶
Add preformatted block. This method adds pre-format tags on BOTH SIDES of the lines presented. You SHOULD provide all lines of one block together in one method call.
type Response ¶
Interface that is returned by the root-level handler. `bytes()` returns the response from the server exactly as it should be sent to the user, including the status code and MIME type, and carriage-return newlines where appropriate.
type ResponseFormat ¶
Formatted response. The status, MIME type, and each line are specified seperately. Note that each string in Lines MUST NOT contain any whitespace or newline characters of it's own, as this will break the formatting.
Note that ResponseFormat uses strings which are UTF-8 encoded. To serve content in other encodings, see ResponsePlain
func (ResponseFormat) Bytes ¶
func (resp ResponseFormat) Bytes() []byte
Response.Bytes() Construct the stream that is sent to the client.
func (ResponseFormat) String ¶
func (resp ResponseFormat) String() string
type ResponsePlain ¶
type ResponsePlain []byte
The developer may use ResponsePlain to have direct control over the output of the handler function. This type is simply a byte slice that will be sent by the server (note that it will not clean the response in any way, such as converting newlines to carriage-return newlines).
func (ResponsePlain) Bytes ¶
func (resp ResponsePlain) Bytes() []byte
func (ResponsePlain) String ¶
func (resp ResponsePlain) String() string
type ResponseRead ¶
type ResponseRead struct { Content io.ReadCloser Mime string Name string }
ResponseRead replies to the request with the contents of an io.ReadCloser (such as an os.File). If an io.Reader is used, see io.NopCloser. The Content is read after the handler function returns this struct, after which ResponseRead.Content will be closed. NOTE: if an error is recieved while reading from ResponseRead.Content, the error message will be shown as the Mime type with Status TemporaryFailure. Otherwise, the response will always have status code Success.
func (ResponseRead) Bytes ¶
func (r ResponseRead) Bytes() []byte
func (ResponseRead) String ¶
func (r ResponseRead) String() string
type Server ¶
type Server struct { Address string // ":1965" etc. Handler // should be reset by user after calling gemini.GetServer Cert []byte // certificate itself, not a filename. Key []byte Shutdown chan byte // send a byte to this channel to initiate the shutdown ShutdownCompleted chan byte // server sends byte on this channel when shutdown is completed Ready chan byte // server sends byte on this channel when the server completes initialization and is listening. ReadLimit int64 // Maximum limit of URL (default is 1024 according to specification) Logger io.Writer // If set (!=nil), log requests to this writer. // contains filtered or unexported fields }
gemini.Server contains information about the server which is used to initiate a TCP listener.
Example ¶
package main import ( "crypto/tls" "fmt" "log" "net/url" "os" "time" "codeberg.org/FiskFan1999/gemini" ) func handler(u *url.URL, c *tls.Conn) gemini.Response { return gemini.ResponseFormat{ gemini.Success, "text/gemini", gemini.Lines{ fmt.Sprintf("%sHello!", gemini.Header), "Welcome to my Gemini capsule!", }, } } func main() { // Load certificates cert, err := os.ReadFile("cert.pem") if err != nil { log.Fatal(err.Error()) } key, err := os.ReadFile("key.pem") if err != nil { log.Fatal(err.Error()) } // initialize and run server serv := gemini.GetServer(":1965", cert, key) serv.Handler = handler go func() { time.Sleep(time.Second) serv.Shutdown <- 0 }() if err := serv.Run(); err != nil { panic(err) } }
Output:
func GetServer ¶
Initialize a server, but does not start it. If `address=""` the server will substitute port `1965` which is the default according to the specification. Note that `cert` and `key` are the texts of the certificates themselves, not filenames.
func (*Server) Run ¶
Run server. This function blocks (does not run in the background) until the server is shut down or there is an error during initialization of the listener. Incoming TCP connections are handled concurrently (note that according to the Gemini specification, the server immediately closes the connection after a single request is handled).
type Status ¶
type Status uint8
Status code of the server's response
const ( Input Status = 10 SensitiveInput Status = 11 Success Status = 20 RedirectTemporary Status = 30 RedirectPermanent Status = 31 TemporaryFailure Status = 40 CGIError Status = 42 ProxyError Status = 43 SlowDown Status = 44 PermanentFailure Status = 50 NotFound Status = 51 Gone Status = 52 ProxyRequestRefused Status = 53 BadRequest Status = 59 ClientCertificateRequired Status = 60 CertificateNotAuthorised Status = 61 CertificateNotValid Status = 62 )
Gemini Status Codes.
func ParseResponse ¶
Parse the response from Client.Get() and return the status and mime-type of the page.
Example ¶
status, mime, err := ParseResponse([]byte("20 text/gemini\r\nHello!")) if err != nil { fmt.Println(err.Error()) } fmt.Printf("status=%d\n", status) fmt.Printf("mime=%q\n", mime)
Output: status=20 mime="text/gemini"
func (Status) Error ¶ added in v0.2.0
Similar as Status.Response, except that it accepts an error, and returns a Response with err.Error() as the mime type. Is friendly, catches if err == nil (doesn't panic).
Example ¶
err := errors.New("Internal server error") resp := TemporaryFailure.Error(err) fmt.Printf("%q\n", resp.Bytes())
Output: "40 Internal server error\r\n"