6
1
mirror of https://git.mills.io/saltyim/saltyim.git synced 2024-06-16 11:58:24 +00:00
prologic-saltyim/utils.go
xuu 754fcc7323 feat: add compression negotiation for sent messages (#91)
feat: add compression negotiation for sent messages
fix: unix homedir handling

the service will negotiate a compression algo for sending messages
when a user chats someone during the auto discovery, the service returns an `Accept-Encoding: br, gzip, deflate`

the client saves that response and so when it makes POSTs of messages adds the best `Content-Encoding` and compresses the message

example:
```
>> GET /.well-known/salty/c765c69040d98f3af2181237f47ec01398d80f8ab2690fe929e4311ab05dec01.json

<< Accept-Encoding: br, gzip, deflate
<<
<< {"endpoint":"https://salty.home.arpa/inbox/01FZBR8Y2E6TH949JA3925WF71","key":"kex1wurry09ftqjuxgjl0jxmqypv4axqvzqljkgeadxjcpwtfuhcedcslck52d"}

>> POST /inbox/01FZBR8Y2E6TH949JA3925WF71
>> Content-Encoding: br
>>
>> [Brotli Compressed data]
```

this PR depends on https://git.mills.io/prologic/msgbus/pulls/24

Co-authored-by: Jon Lundy <jon@xuu.cc>
Reviewed-on: https://git.mills.io/saltyim/saltyim/pulls/91
Co-authored-by: xuu <xuu@noreply@mills.io>
Co-committed-by: xuu <xuu@noreply@mills.io>
2022-03-29 22:23:16 +00:00

104 lines
2.2 KiB
Go

package saltyim
import (
"bytes"
"compress/gzip"
"crypto/rand"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/andybalholm/brotli"
"github.com/oklog/ulid/v2"
log "github.com/sirupsen/logrus"
)
const (
defaultRequestTimeout = time.Second * 30
)
// GenerateULID generates a new unique identifer
func GenerateULID() (string, error) {
entropy := rand.Reader
id, err := ulid.New(ulid.Timestamp(time.Now()), entropy)
if err != nil {
return "", fmt.Errorf("error generating ulid: %w", err)
}
return id.String(), nil
}
// MustGenerateULID generates a new unique identifer or fails
func MustGenerateULID() string {
ulid, err := GenerateULID()
if err != nil {
log.WithError(err).Fatal("error generating ulid")
}
return ulid
}
// Request is a generic request handling function for making artbitrary HTPT
// requests to Salty endpoints for looking up Salty Addresses, Configs and
// publishing encrypted messages.
func Request(method, uri string, headers http.Header, body io.Reader) (*http.Response, error) {
if headers == nil {
headers = make(http.Header)
}
if body != nil {
switch headers.Get("Content-Encoding") {
case "br":
buf := &bytes.Buffer{}
br := brotli.NewWriter(buf)
io.Copy(br, body)
br.Close()
body = buf
case "gzip":
buf := &bytes.Buffer{}
gz := gzip.NewWriter(buf)
io.Copy(gz, body)
gz.Close()
body = buf
}
}
req, err := http.NewRequest(method, uri, body)
if err != nil {
return nil, fmt.Errorf("%s: http.NewRequest fail: %s", uri, err)
}
// Set a default User-Agent (if none set)
if headers.Get("User-Agent") == "" {
headers.Set("User-Agent", fmt.Sprintf("saltyim/%s", FullVersion()))
}
req.Header = headers
client := http.Client{
Timeout: defaultRequestTimeout,
}
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("%s: client.Do fail: %s", uri, err)
}
if res.StatusCode/100 != 2 {
return nil, fmt.Errorf("non-2xx response received: %s", res.Status)
}
return res, nil
}
// SplitInbox splits and endpoint into it's components (inbox, uri)
// where inbox is a topic queue on the Salty broker uri
func SplitInbox(endpoint string) (string, string) {
idx := strings.LastIndex(endpoint, "/")
if idx == -1 {
return "", ""
}
return endpoint[:idx], endpoint[idx+1:]
}