You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.3 KiB
85 lines
2.3 KiB
package main |
|
|
|
import ( |
|
"bufio" |
|
"crypto/tls" |
|
"errors" |
|
"mime" |
|
"strconv" |
|
|
|
//"errors" |
|
"net/url" |
|
"strings" |
|
|
|
"github.com/fatih/color" |
|
) |
|
|
|
type GeminiResponse struct { |
|
status int |
|
meta string // not parsed into mediaType and params yet |
|
bodyReader *bufio.Reader |
|
bodyReaderClosed bool // I have no idea what I'm doing here |
|
conn *tls.Conn |
|
connClosed bool |
|
} |
|
|
|
// these should be used but atm it isnt, lol |
|
//var ( |
|
//ErrConnFail = errors.New("connection failed") |
|
//ErrInvalidStatus = errors.New("invalid status code") |
|
//ErrDecodeMetaFail = errors.New("failed to decode meta header") |
|
//) |
|
|
|
var ( |
|
h1Style = color.New(color.Bold).Add(color.Underline).Add(color.FgYellow).SprintFunc() |
|
h2Style = color.New(color.Bold).SprintFunc() |
|
linkStyle = color.New(color.FgBlue).SprintFunc() |
|
// style only applied to first line for some reason, so removing it all together :P |
|
// quoteStyle = color.New(color.Italic).SprintFunc() |
|
) |
|
|
|
// GeminiParsedURL fetches u and retruns *GeminiResponse |
|
func GeminiParsedURL(u url.URL) (res *GeminiResponse, err error) { |
|
host := u.Host |
|
// Connect to server |
|
if u.Port() == "" { |
|
host += ":1965" |
|
} |
|
// TODO: cert cache and stuff |
|
conn, err := tls.Dial("tcp", host, &tls.Config{InsecureSkipVerify: true}) |
|
if err != nil { |
|
return |
|
} |
|
// defer conn.Close() |
|
// Send request |
|
conn.Write([]byte(u.String() + "\r\n")) |
|
// Receive and parse response header |
|
reader := bufio.NewReader(conn) |
|
responseHeader, err := reader.ReadString('\n') |
|
// Parse header |
|
parts := strings.Fields(responseHeader) |
|
status, err := strconv.Atoi(parts[0]) |
|
if err != nil { |
|
conn.Close() |
|
return res, errors.New("Invalid status code") |
|
} |
|
meta := strings.Join(parts[1:], " ") |
|
res = &GeminiResponse{status, meta, reader, false, conn, false} |
|
return |
|
} |
|
|
|
// ParseMeta returns the output of mime.ParseMediaType, but handles the empty |
|
// META which is equal to "text/gemini; charset=utf-8" according to the spec. |
|
func ParseMeta(meta string) (string, map[string]string, error) { |
|
if meta == "" { |
|
return "text/gemini", make(map[string]string), nil |
|
} |
|
|
|
mediatype, params, err := mime.ParseMediaType(meta) |
|
if mediatype != "" && err != nil { |
|
// The mediatype was successfully decoded but there's some error with the params |
|
// Ignore the params |
|
return mediatype, make(map[string]string), nil |
|
} |
|
return mediatype, params, err |
|
}
|
|
|