aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoradnano <[email protected]>2020-09-21 22:09:50 -0400
committeradnano <[email protected]>2020-09-21 22:09:50 -0400
commit86e7fe435523bc01dfab12746da9c92dbbfea778 (patch)
tree3037d829fbce283fbefed9aabc6d93f70827c5dc
parentRefactor client requests and server responses (diff)
downloadgo-gemini-86e7fe435523bc01dfab12746da9c92dbbfea778.tar.xz
go-gemini-86e7fe435523bc01dfab12746da9c92dbbfea778.zip
Add package declaration comment
-rw-r--r--README.md4
-rw-r--r--client.go59
-rw-r--r--gemini.go132
-rw-r--r--server.go56
4 files changed, 137 insertions, 114 deletions
diff --git a/README.md b/README.md
index 638115e..f98a442 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
[![GoDoc](https://godoc.org/git.sr.ht/~adnano/go-gemini?status.svg)](https://godoc.org/git.sr.ht/~adnano/go-gemini)
-`go-gemini` implements the [Gemini protocol](https://gemini.circumlunar.space) in
-Go.
+`go-gemini` implements the [Gemini protocol](https://gemini.circumlunar.space)
+in Go.
It aims to provide an API similar to that of `net/http` to make it easy to
develop Gemini clients and servers.
diff --git a/client.go b/client.go
index 7585dfc..ecd91ee 100644
--- a/client.go
+++ b/client.go
@@ -3,18 +3,11 @@ package gemini
import (
"bytes"
"crypto/tls"
- "errors"
"io/ioutil"
- "net/url"
"strconv"
"strings"
)
-var (
- ErrProtocol = errors.New("Protocol error")
- ErrInvalidURL = errors.New("Invalid URL")
-)
-
// Client is a Gemini client.
type Client struct{}
@@ -44,55 +37,6 @@ func (c *Client) ProxyRequest(host, url string) (*Response, error) {
return c.Do(req)
}
-// Request is a Gemini request.
-//
-// A Request can optionally be configured with a client certificate. Example:
-//
-// req := NewRequest(url)
-// cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
-// if err != nil {
-// panic(err)
-// }
-// req.Certificates = append(req.Certificates, cert)
-//
-type Request struct {
- Host string // host or host:port
- URL *url.URL // the requested URL
- Certificates []tls.Certificate // client certificates
-}
-
-// NewRequest returns a new request. The host is inferred from the provided url.
-func NewRequest(rawurl string) (*Request, error) {
- u, err := url.Parse(rawurl)
- if err != nil {
- return nil, err
- }
-
- // Ignore UserInfo if present
- u.User = nil
-
- return &Request{
- Host: u.Host,
- URL: u,
- }, nil
-}
-
-// NewProxyRequest makes a new request using the provided host.
-func NewProxyRequest(host, rawurl string) (*Request, error) {
- u, err := url.Parse(rawurl)
- if err != nil {
- return nil, err
- }
-
- // Ignore UserInfo if present
- u.User = nil
-
- return &Request{
- Host: host,
- URL: u,
- }, nil
-}
-
// Do sends a Gemini request and returns a Gemini response.
func (c *Client) Do(req *Request) (*Response, error) {
host := req.Host
@@ -113,8 +57,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
defer conn.Close()
// Write the request
- request := req.URL.String() + "\r\n"
- if _, err := conn.Write([]byte(request)); err != nil {
+ if err := req.Write(conn); err != nil {
return nil, err
}
diff --git a/gemini.go b/gemini.go
new file mode 100644
index 0000000..c39474e
--- /dev/null
+++ b/gemini.go
@@ -0,0 +1,132 @@
+// Package gemini implements the Gemini protocol.
+package gemini
+
+import (
+ "crypto/tls"
+ "errors"
+ "io"
+ "net/url"
+ "strconv"
+)
+
+// Status codes.
+const (
+ StatusInput = 10
+ StatusSensitiveInput = 11
+ StatusSuccess = 20
+ StatusRedirectTemporary = 30
+ StatusRedirectPermanent = 31
+ StatusTemporaryFailure = 40
+ StatusServerUnavailable = 41
+ StatusCGIError = 42
+ StatusProxyError = 43
+ StatusSlowDown = 44
+ StatusPermanentFailure = 50
+ StatusNotFound = 51
+ StatusGone = 52
+ StatusProxyRequestRefused = 53
+ StatusBadRequest = 59
+ StatusClientCertificateRequired = 60
+ StatusCertificateNotAuthorised = 61
+ StatusCertificateNotValid = 62
+)
+
+// Status code categories.
+const (
+ StatusClassInput = 1
+ StatusClassSuccess = 2
+ StatusClassRedirect = 3
+ StatusClassTemporaryFailure = 4
+ StatusClassPermanentFailure = 5
+ StatusClassClientCertificateRequired = 6
+)
+
+// Errors.
+var (
+ ErrProtocol = errors.New("Protocol error")
+ ErrInvalidURL = errors.New("Invalid URL")
+)
+
+// Request is a Gemini request.
+//
+// A Request can optionally be configured with a client certificate. Example:
+//
+// req := NewRequest(url)
+// cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
+// if err != nil {
+// panic(err)
+// }
+// req.Certificates = append(req.Certificates, cert)
+//
+type Request struct {
+ Host string // host or host:port
+ URL *url.URL // the requested URL
+ Certificates []tls.Certificate // client certificates
+}
+
+// NewRequest returns a new request. The host is inferred from the provided url.
+func NewRequest(rawurl string) (*Request, error) {
+ u, err := url.Parse(rawurl)
+ if err != nil {
+ return nil, err
+ }
+
+ // UserInfo is invalid
+ if u.User != nil {
+ return nil, ErrInvalidURL
+ }
+
+ return &Request{
+ Host: u.Host,
+ URL: u,
+ }, nil
+}
+
+// NewProxyRequest returns a new request using the provided host.
+func NewProxyRequest(host, rawurl string) (*Request, error) {
+ u, err := url.Parse(rawurl)
+ if err != nil {
+ return nil, err
+ }
+
+ // UserInfo is invalid
+ if u.User != nil {
+ return nil, ErrInvalidURL
+ }
+
+ return &Request{
+ Host: host,
+ URL: u,
+ }, nil
+}
+
+// Write writes the Gemini request to the provided io.Writer.
+func (r *Request) Write(w io.Writer) error {
+ request := r.URL.String() + "\r\n"
+ _, err := w.Write([]byte(request))
+ return err
+}
+
+// Response is a Gemini response.
+type Response struct {
+ Status int
+ Meta string
+ Body []byte
+}
+
+// Write writes the Gemini response header and body to the provided io.Writer.
+func (r *Response) Write(w io.Writer) error {
+ header := strconv.Itoa(r.Status) + " " + r.Meta + "\r\n"
+ if _, err := w.Write([]byte(header)); err != nil {
+ return err
+ }
+
+ // Only write the response body on success
+ if r.Status/10 == StatusClassSuccess {
+ if _, err := w.Write(r.Body); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/server.go b/server.go
index ad93738..3de09df 100644
--- a/server.go
+++ b/server.go
@@ -3,63 +3,11 @@ package gemini
import (
"crypto/tls"
"crypto/x509"
- "io"
"net"
"net/url"
- "strconv"
"strings"
)
-// Status codes.
-const (
- StatusInput = 10
- StatusSensitiveInput = 11
- StatusSuccess = 20
- StatusRedirectTemporary = 30
- StatusRedirectPermanent = 31
- StatusTemporaryFailure = 40
- StatusServerUnavailable = 41
- StatusCGIError = 42
- StatusProxyError = 43
- StatusSlowDown = 44
- StatusPermanentFailure = 50
- StatusNotFound = 51
- StatusGone = 52
- StatusProxyRequestRefused = 53
- StatusBadRequest = 59
- StatusClientCertificateRequired = 60
- StatusCertificateNotAuthorised = 61
- StatusCertificateNotValid = 62
-)
-
-// Status code categories.
-const (
- StatusClassInput = 1
- StatusClassSuccess = 2
- StatusClassRedirect = 3
- StatusClassTemporaryFailure = 4
- StatusClassPermanentFailure = 5
- StatusClassClientCertificateRequired = 6
-)
-
-// Response is a Gemini response.
-type Response struct {
- Status int
- Meta string
- Body []byte
-}
-
-// Write writes the Gemini response header and body to the provided io.Writer.
-func (r *Response) Write(w io.Writer) {
- header := strconv.Itoa(r.Status) + " " + r.Meta + "\r\n"
- w.Write([]byte(header))
-
- // Only write the response body on success
- if r.Status/10 == StatusClassSuccess {
- w.Write(r.Body)
- }
-}
-
// Server is a Gemini server.
type Server struct {
Addr string
@@ -128,7 +76,7 @@ func (s *Server) Serve(ln net.Listener) error {
type RequestInfo struct {
URL *url.URL // the requested URL
Certificates []*x509.Certificate // client certificates
- RemoteAddr net.Addr
+ RemoteAddr net.Addr // client remote address
}
// A Handler responds to a Gemini request.
@@ -163,7 +111,7 @@ func (m *Mux) match(url *url.URL) Handler {
return nil
}
-// Handle registers a handler for the given pattern.
+// Handle registers a Handler for the given pattern.
func (m *Mux) Handle(pattern string, handler Handler) {
url, err := url.Parse(pattern)
if err != nil {