package human import ( "context" "encoding/json" "errors" "io" "net/http" "net/url" "strings" ) var ErrUnsupportedContentType = errors.New("unsupported content type") // GetHumanFromHTML returns the [Human] contained in the [http.Response]. // // If Content-Type is unsupported, it returns [ErrUnsupportedContentType]. // Currently, only `text/html` is supported. // // [http.Client] is used to retrieve the `human.json` linked in the [http.Response.Body]. func GetHumanFromHTML(ctx context.Context, client *http.Client, resp *http.Response) (*Human, error) { if !strings.Contains(resp.Header.Get("Content-Type"), "text/html") { return nil, ErrUnsupportedContentType } b, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { return nil, err } url, err := GetURLFromHTML(b, resp.Request.URL) if err != nil { return nil, err } if url == nil { return nil, nil } req, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil) if err != nil { return nil, err } resp, err = client.Do(req) if err != nil { return nil, err } b, err = io.ReadAll(resp.Body) resp.Body.Close() if err != nil { return nil, err } var h Human return &h, json.Unmarshal(b, &h) } // GetURLFromHTML returns the [url.URL] extracted from the raw HTML. // Base is the URL containing the HTML. // // Returns nil if nothing is found. func GetURLFromHTML(b []byte, base *url.URL) (*url.URL, error) { content := string(b) runed := []rune(content) i := strings.Index(content, `= 0 && i+6 < len(runed) { content = string(runed[i+6:]) runed = []rune(content) args := parseArgs(content) if args["rel"] == "human-json" { href, ok := args["href"] if ok { u, err := url.Parse(href) if err != nil { return nil, err } if u.Host != "" { return u, nil } path := u.Path *u = *base if strings.HasPrefix(path, "/") { u.Path = path } else { u = u.JoinPath(path) } return u, nil } } i = strings.Index(content, ` 0 { res[key.String()] = "" } key.Reset() default: key.WriteRune(curr) } } else { if value.Len() == 0 && curr == '"' { quote = true } else if (curr == '"' && quote) || curr == ' ' && !quote { quote = false sep = false res[key.String()] = value.String() key.Reset() value.Reset() } else { value.WriteRune(curr) } } i++ } if key.Len() > 0 { res[key.String()] = value.String() } return res }