saltyim is the Go library and reference client and broker implementation for Salty IM it contains a command-line client (cli), a terminal user interface (tui), builtin server/broker and a Mobile / Desktop App PWA (progressive web app) https://salty.im/
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.
 
 
 
 
 
saltyim/utils.go

103 lines
2.2 KiB

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:]
}