2020-10-24 19:15:32 +00:00
|
|
|
package gemini
|
2020-10-21 21:07:28 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"crypto/tls"
|
2020-12-18 06:41:14 +00:00
|
|
|
"io"
|
2020-10-21 21:07:28 +00:00
|
|
|
"net/url"
|
|
|
|
)
|
|
|
|
|
2021-02-14 20:50:41 +00:00
|
|
|
// A Request represents a Gemini request received by a server or to be sent
|
|
|
|
// by a client.
|
2020-10-21 21:07:28 +00:00
|
|
|
type Request struct {
|
2021-02-23 23:45:58 +00:00
|
|
|
// URL specifies the URL being requested.
|
2020-10-21 21:07:28 +00:00
|
|
|
URL *url.URL
|
|
|
|
|
2021-02-15 00:02:34 +00:00
|
|
|
// For client requests, Host optionally specifies the server to
|
2021-02-20 18:30:55 +00:00
|
|
|
// connect to. It may be of the form "host" or "host:port".
|
2021-02-15 00:02:34 +00:00
|
|
|
// If empty, the value of URL.Host is used.
|
|
|
|
// For international domain names, Host may be in Punycode or
|
|
|
|
// Unicode form. Use golang.org/x/net/idna to convert it to
|
|
|
|
// either format if needed.
|
2021-02-23 23:45:58 +00:00
|
|
|
// This field is ignored by the Gemini server.
|
2020-10-21 21:07:28 +00:00
|
|
|
Host string
|
|
|
|
|
2021-02-14 22:34:57 +00:00
|
|
|
// For client requests, Certificate optionally specifies the
|
|
|
|
// TLS certificate to present to the other side of the connection.
|
|
|
|
// This field is ignored by the Gemini server.
|
2020-10-21 21:07:28 +00:00
|
|
|
Certificate *tls.Certificate
|
2021-02-24 13:24:47 +00:00
|
|
|
|
2023-05-26 04:38:12 +00:00
|
|
|
TLS *tls.ConnectionState
|
2020-10-21 21:07:28 +00:00
|
|
|
}
|
|
|
|
|
2021-02-15 00:02:34 +00:00
|
|
|
// NewRequest returns a new request.
|
2021-02-14 22:34:57 +00:00
|
|
|
// The returned Request is suitable for use with Client.Do.
|
2021-02-15 22:23:54 +00:00
|
|
|
//
|
|
|
|
// Callers should be careful that the URL query is properly escaped.
|
|
|
|
// See the documentation for QueryEscape for more information.
|
2020-10-21 21:07:28 +00:00
|
|
|
func NewRequest(rawurl string) (*Request, error) {
|
|
|
|
u, err := url.Parse(rawurl)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-15 22:23:54 +00:00
|
|
|
return &Request{URL: u}, nil
|
2020-10-21 21:07:28 +00:00
|
|
|
}
|
|
|
|
|
2021-02-14 20:50:41 +00:00
|
|
|
// ReadRequest reads and parses an incoming request from r.
|
|
|
|
//
|
|
|
|
// ReadRequest is a low-level function and should only be used
|
|
|
|
// for specialized applications; most code should use the Server
|
|
|
|
// to read requests and handle them via the Handler interface.
|
2020-12-18 06:41:14 +00:00
|
|
|
func ReadRequest(r io.Reader) (*Request, error) {
|
2021-03-20 16:27:20 +00:00
|
|
|
// Limit request size
|
2021-02-15 05:16:21 +00:00
|
|
|
r = io.LimitReader(r, 1026)
|
2021-02-14 20:50:41 +00:00
|
|
|
br := bufio.NewReaderSize(r, 1026)
|
2021-03-20 16:27:20 +00:00
|
|
|
b, err := br.ReadBytes('\n')
|
2020-12-18 06:41:14 +00:00
|
|
|
if err != nil {
|
2021-03-20 16:27:20 +00:00
|
|
|
if err == io.EOF {
|
|
|
|
return nil, ErrInvalidRequest
|
|
|
|
}
|
2020-12-18 06:41:14 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-03-20 16:27:20 +00:00
|
|
|
// Read URL
|
|
|
|
rawurl, ok := trimCRLF(b)
|
|
|
|
if !ok {
|
2020-12-18 06:41:14 +00:00
|
|
|
return nil, ErrInvalidRequest
|
|
|
|
}
|
2021-03-20 16:27:20 +00:00
|
|
|
if len(rawurl) == 0 {
|
2020-12-18 06:41:14 +00:00
|
|
|
return nil, ErrInvalidRequest
|
|
|
|
}
|
2021-03-20 16:27:20 +00:00
|
|
|
u, err := url.Parse(string(rawurl))
|
2020-12-18 06:41:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &Request{URL: u}, nil
|
|
|
|
}
|
|
|
|
|
2021-03-01 03:16:38 +00:00
|
|
|
// WriteTo writes r to w in the Gemini request format.
|
2021-02-14 20:50:41 +00:00
|
|
|
// This method consults the request URL only.
|
2021-03-01 03:20:43 +00:00
|
|
|
func (r *Request) WriteTo(w io.Writer) (int64, error) {
|
2021-02-19 02:58:35 +00:00
|
|
|
bw := bufio.NewWriterSize(w, 1026)
|
2021-02-15 04:33:16 +00:00
|
|
|
url := r.URL.String()
|
|
|
|
if len(url) > 1024 {
|
2021-03-01 03:16:38 +00:00
|
|
|
return 0, ErrInvalidRequest
|
2021-02-15 04:33:16 +00:00
|
|
|
}
|
2021-03-01 03:20:43 +00:00
|
|
|
var wrote int64
|
2021-03-01 03:16:38 +00:00
|
|
|
n, err := bw.WriteString(url)
|
2021-03-01 03:20:43 +00:00
|
|
|
wrote += int64(n)
|
2021-03-01 03:16:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return wrote, err
|
2020-10-21 21:07:28 +00:00
|
|
|
}
|
2021-03-01 03:16:38 +00:00
|
|
|
n, err = bw.Write(crlf)
|
2021-03-01 03:20:43 +00:00
|
|
|
wrote += int64(n)
|
2021-03-01 03:16:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return wrote, err
|
2020-10-21 21:07:28 +00:00
|
|
|
}
|
2021-03-01 03:16:38 +00:00
|
|
|
return wrote, bw.Flush()
|
2020-10-21 21:07:28 +00:00
|
|
|
}
|
2021-02-24 13:24:47 +00:00
|
|
|
|
2021-02-27 19:02:30 +00:00
|
|
|
// ServerName returns the value of the TLS Server Name Indication extension
|
|
|
|
// sent by the client.
|
2021-03-01 02:59:17 +00:00
|
|
|
// ServerName returns an empty string for client requests.
|
2021-02-27 19:02:30 +00:00
|
|
|
func (r *Request) ServerName() string {
|
2023-05-26 04:38:12 +00:00
|
|
|
if tls := r.TLS; tls != nil {
|
2021-02-27 19:02:30 +00:00
|
|
|
return tls.ServerName
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|