1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
package main
import (
"fmt"
"log"
"net"
"net/http"
"os/exec"
"strings"
"time"
"git.sr.ht/~emersion/gqlclient"
"github.com/spf13/cobra"
)
type Client struct {
*gqlclient.Client
BaseURL string
HTTP *http.Client
}
func createClient(service string, cmd *cobra.Command) *Client {
return createClientWithInstance(service, cmd, "")
}
func createClientWithInstance(service string, cmd *cobra.Command, instanceName string) *Client {
cfg := loadConfig(cmd)
if len(cfg.Instances) == 0 {
log.Fatalf("no sr.ht instance configured")
}
if instanceFlag, err := cmd.Flags().GetString("instance"); err != nil {
log.Fatal(err)
} else if instanceFlag != "" {
if instanceName != "" && !instancesEqual(instanceName, instanceFlag) {
log.Fatalf("conflicting instances: %v and --instance=%v", instanceName, instanceFlag)
}
instanceName = instanceFlag
}
var inst *InstanceConfig
if instanceName != "" {
for _, instance := range cfg.Instances {
if instance.match(instanceName) {
inst = instance
break
}
}
if inst == nil {
log.Fatalf("no instance for %s found", instanceName)
}
} else {
inst = cfg.Instances[0]
}
var token string
if len(inst.AccessTokenCmd) > 0 {
cmd := exec.Command(inst.AccessTokenCmd[0], inst.AccessTokenCmd[1:]...)
output, err := cmd.Output()
if err != nil {
log.Fatalf("could not execute access-token-cmd: %v", err)
}
fields := strings.Fields(string(output))
if len(fields) == 0 {
log.Fatalf("access-token-cmd did not return a token")
}
token = fields[0]
} else {
token = inst.AccessToken
}
var baseURL string
if serviceCfg := inst.Services()[service]; serviceCfg != nil {
baseURL = serviceCfg.Origin
}
if baseURL == "" && strings.Contains(inst.Name, ".") && net.ParseIP(inst.Name) == nil {
baseURL = fmt.Sprintf("https://%s.%s", service, inst.Name)
}
if baseURL == "" {
log.Fatalf("failed to get origin for service %q in instance %q", service, inst.Name)
}
debug, err := cmd.Flags().GetBool("debug")
if err != nil {
log.Fatal(err)
}
return createClientWithToken(baseURL, token, debug)
}
func createClientWithToken(baseURL, token string, debug bool) *Client {
gqlEndpoint := baseURL + "/query"
httpClient := &http.Client{
Transport: &httpTransport{accessToken: token, logRequest: debug},
Timeout: 30 * time.Second,
}
return &Client{
Client: gqlclient.New(gqlEndpoint, httpClient),
BaseURL: baseURL,
HTTP: httpClient,
}
}
type httpTransport struct {
accessToken string
logRequest bool
count int
}
func (tr *httpTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("User-Agent", "hut/"+version)
req.Header.Set("Authorization", "Bearer "+tr.accessToken)
// Add delay to consecutive API requests to keep hut from DoSing the server
if tr.count > 0 {
time.Sleep(time.Second)
}
tr.count++
if tr.logRequest {
log.Println(req.Body)
}
return http.DefaultTransport.RoundTrip(req)
}
|