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