summaryrefslogtreecommitdiff
path: root/gemini/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'gemini/client.go')
-rw-r--r--gemini/client.go68
1 files changed, 66 insertions, 2 deletions
diff --git a/gemini/client.go b/gemini/client.go
index 0a5dd76..c60e92e 100644
--- a/gemini/client.go
+++ b/gemini/client.go
@@ -7,6 +7,8 @@ import (
"io"
"net"
neturl "net/url"
+ "strconv"
+ "strings"
"tildegit.org/tjp/sliderule/internal/types"
)
@@ -48,7 +50,7 @@ var ExceededMaxRedirects = errors.New("gemini.Client: exceeded MaxRedirects")
// This method will not automatically follow redirects or cache permanent failures or
// redirects.
func (client Client) RoundTrip(request *types.Request) (*types.Response, error) {
- if request.Scheme != "gemini" && request.Scheme != "" {
+ if request.Scheme != "gemini" && request.Scheme != "titan" && request.Scheme != "" {
return nil, errors.New("non-gemini protocols not supported")
}
@@ -72,7 +74,33 @@ func (client Client) RoundTrip(request *types.Request) (*types.Response, error)
st := conn.ConnectionState()
request.TLSState = &st
- if _, err := conn.Write([]byte(request.URL.String() + "\r\n")); err != nil {
+ destURL := *request.URL
+
+ var body []byte
+ if request.Scheme == "titan" {
+ var err error
+ if bodyrdr, ok := request.Meta.(io.Reader); ok {
+ body, err = io.ReadAll(bodyrdr)
+ if err != nil {
+ return nil, err
+ }
+ if err := close(request.Meta); err != nil {
+ return nil, err
+ }
+
+ path, params := pathparams(destURL.Path)
+ params["size"] = strconv.Itoa(len(body))
+ destURL.Path = assemblepath(path, params)
+ } else {
+ body = []byte{}
+ }
+ }
+
+ if _, err := conn.Write([]byte(destURL.String() + "\r\n")); err != nil {
+ return nil, err
+ }
+
+ if _, err := conn.Write(body); err != nil {
return nil, err
}
@@ -124,3 +152,39 @@ func (c Client) Fetch(url string) (*types.Response, error) {
func (c Client) IsRedirect(response *types.Response) bool {
return ResponseCategoryForStatus(response.Status) == ResponseCategoryRedirect
}
+
+func pathparams(basepath string) (string, map[string]string) {
+ params := map[string]string{}
+ path, paramstr, found := strings.Cut(basepath, ";")
+ if !found {
+ return path, params
+ }
+
+ for _, pairstr := range strings.Split(paramstr, ";") {
+ key, val, found := strings.Cut(pairstr, "=")
+ if found {
+ params[key] = val
+ }
+ }
+
+ return path, params
+}
+
+func assemblepath(basepath string, params map[string]string) string {
+ path := strings.Builder{}
+ _, _ = path.WriteString(basepath)
+ for key, val := range params {
+ _ = path.WriteByte(';')
+ _, _ = path.WriteString(key)
+ _ = path.WriteByte('=')
+ _, _ = path.WriteString(val)
+ }
+ return path.String()
+}
+
+func close(closer any) error {
+ if cl, ok := closer.(io.Closer); ok {
+ return cl.Close()
+ }
+ return nil
+}