156 lines
2.9 KiB
Go
156 lines
2.9 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
http "github.com/valyala/fasthttp"
|
|
|
|
"source.toby3d.me/website/indieauth/internal/client"
|
|
"source.toby3d.me/website/indieauth/internal/domain"
|
|
"source.toby3d.me/website/indieauth/internal/util"
|
|
)
|
|
|
|
type httpClientRepository struct {
|
|
client *http.Client
|
|
}
|
|
|
|
const DefaultMaxRedirectsCount int = 10
|
|
|
|
const (
|
|
relRedirectURI string = "redirect_uri"
|
|
|
|
hApp string = "h-app"
|
|
hXApp string = "h-x-app"
|
|
|
|
propertyLogo string = "logo"
|
|
propertyName string = "name"
|
|
propertyURL string = "url"
|
|
)
|
|
|
|
func NewHTTPClientRepository(c *http.Client) client.Repository {
|
|
return &httpClientRepository{
|
|
client: c,
|
|
}
|
|
}
|
|
|
|
func (repo *httpClientRepository) Get(ctx context.Context, id *domain.ClientID) (*domain.Client, error) {
|
|
req := http.AcquireRequest()
|
|
defer http.ReleaseRequest(req)
|
|
req.SetRequestURI(id.String())
|
|
req.Header.SetMethod(http.MethodGet)
|
|
|
|
resp := http.AcquireResponse()
|
|
defer http.ReleaseResponse(resp)
|
|
|
|
if err := repo.client.DoRedirects(req, resp, DefaultMaxRedirectsCount); err != nil {
|
|
return nil, fmt.Errorf("failed to make a request to the client: %w", err)
|
|
}
|
|
|
|
if resp.StatusCode() == http.StatusNotFound {
|
|
return nil, client.ErrNotExist
|
|
}
|
|
|
|
client := &domain.Client{
|
|
ID: id,
|
|
RedirectURI: make([]*domain.URL, 0),
|
|
Logo: make([]*domain.URL, 0),
|
|
URL: make([]*domain.URL, 0),
|
|
Name: make([]string, 0),
|
|
}
|
|
|
|
extract(client, resp)
|
|
|
|
return client, nil
|
|
}
|
|
|
|
func extract(dst *domain.Client, src *http.Response) {
|
|
for _, u := range util.ExtractEndpoints(src, relRedirectURI) {
|
|
if containsURL(dst.RedirectURI, u) {
|
|
continue
|
|
}
|
|
|
|
dst.RedirectURI = append(dst.RedirectURI, u)
|
|
}
|
|
|
|
for _, t := range []string{hXApp, hApp} {
|
|
for _, name := range util.ExtractProperty(src, t, propertyName) {
|
|
n, ok := name.(string)
|
|
if !ok || containsString(dst.Name, n) {
|
|
continue
|
|
}
|
|
|
|
dst.Name = append(dst.Name, n)
|
|
}
|
|
|
|
for _, logo := range util.ExtractProperty(src, t, propertyLogo) {
|
|
var err error
|
|
|
|
var u *domain.URL
|
|
switch l := logo.(type) {
|
|
case string:
|
|
u, err = domain.ParseURL(l)
|
|
case map[string]string:
|
|
value, ok := l["value"]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
u, err = domain.ParseURL(value)
|
|
}
|
|
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if containsURL(dst.Logo, u) {
|
|
continue
|
|
}
|
|
|
|
dst.Logo = append(dst.Logo, u)
|
|
}
|
|
|
|
for _, url := range util.ExtractProperty(src, t, propertyURL) {
|
|
l, ok := url.(string)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
u, err := domain.ParseURL(l)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if containsURL(dst.URL, u) {
|
|
continue
|
|
}
|
|
|
|
dst.URL = append(dst.URL, u)
|
|
}
|
|
}
|
|
}
|
|
|
|
func containsString(src []string, find string) bool {
|
|
for i := range src {
|
|
if src[i] != find {
|
|
continue
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func containsURL(src []*domain.URL, find *domain.URL) bool {
|
|
for i := range src {
|
|
if src[i].String() != find.String() {
|
|
continue
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|