Copied indieauth pkg from ekster
This commit is contained in:
commit
2569e2d18d
185
auth.go
Normal file
185
auth.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
package indieauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"linkheader"
|
||||
|
||||
"p83.nl/go/ekster/pkg/util"
|
||||
"willnorris.com/go/microformats"
|
||||
)
|
||||
|
||||
type Endpoints struct {
|
||||
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||
TokenEndpoint string `json:"token_endpoint"`
|
||||
MicropubEndpoint string `json:"micropub_endpoint"`
|
||||
MicrosubEndpoint string `json:"microsub_endpoint"`
|
||||
}
|
||||
|
||||
type TokenResponse struct {
|
||||
Me string `json:"me"`
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
func GetEndpoints(me *url.URL) (Endpoints, error) {
|
||||
var endpoints Endpoints
|
||||
|
||||
baseURL := me
|
||||
|
||||
res, err := http.Get(me.String())
|
||||
if err != nil {
|
||||
return endpoints, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var links linkheader.Links
|
||||
|
||||
if headers, e := res.Header["Link"]; e {
|
||||
links = linkheader.ParseMultiple(headers)
|
||||
for _, link := range links {
|
||||
if link.Rel == "authorization_endpoint" {
|
||||
endpoints.AuthorizationEndpoint = link.URL
|
||||
} else if link.Rel == "token_endpoint" {
|
||||
endpoints.TokenEndpoint = link.URL
|
||||
} else if link.Rel == "micropub" {
|
||||
endpoints.MicropubEndpoint = link.URL
|
||||
} else if link.Rel == "microsub" {
|
||||
endpoints.MicrosubEndpoint = link.URL
|
||||
} else {
|
||||
log.Printf("Skipping unsupported rels in Link header: %s %s\n", link.Rel, link.URL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data := microformats.Parse(res.Body, baseURL)
|
||||
|
||||
if auth, e := data.Rels["authorization_endpoint"]; e && endpoints.AuthorizationEndpoint == "" {
|
||||
endpoints.AuthorizationEndpoint = auth[0]
|
||||
}
|
||||
if token, e := data.Rels["token_endpoint"]; e && endpoints.TokenEndpoint == "" {
|
||||
endpoints.TokenEndpoint = token[0]
|
||||
}
|
||||
if micropub, e := data.Rels["micropub"]; e && endpoints.MicropubEndpoint == "" {
|
||||
endpoints.MicropubEndpoint = micropub[0]
|
||||
}
|
||||
if microsub, e := data.Rels["microsub"]; e && endpoints.MicrosubEndpoint == "" {
|
||||
endpoints.MicrosubEndpoint = microsub[0]
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
func Authorize(me *url.URL, endpoints Endpoints, clientID, scope string) (TokenResponse, error) {
|
||||
var tokenResponse TokenResponse
|
||||
|
||||
authURL, err := url.Parse(endpoints.AuthorizationEndpoint)
|
||||
if err != nil {
|
||||
return tokenResponse, err
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return tokenResponse, err
|
||||
}
|
||||
|
||||
local := ln.Addr().String()
|
||||
redirectURI := fmt.Sprintf("http://%s/", local)
|
||||
state := util.RandStringBytes(16)
|
||||
|
||||
authorizationURL := CreateAuthorizationURL(*authURL, me.String(), clientID, redirectURI, state, scope)
|
||||
|
||||
log.Printf("Browse to %s\n", authorizationURL)
|
||||
|
||||
shutdown := make(chan struct{}, 1)
|
||||
|
||||
code := ""
|
||||
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
code = r.URL.Query().Get("code")
|
||||
responseState := r.URL.Query().Get("state")
|
||||
if state != responseState {
|
||||
log.Println("Wrong state response")
|
||||
}
|
||||
fmt.Fprintln(w, `<div style="width:100%;height:100%;display: flex; align-items: center; justify-content: center;">You can close this window, proceed on the command line</div>`)
|
||||
close(shutdown)
|
||||
}
|
||||
|
||||
var srv http.Server
|
||||
srv.Handler = http.HandlerFunc(handler)
|
||||
|
||||
idleConnsClosed := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
<-shutdown
|
||||
|
||||
// We received an interrupt signal, shut down.
|
||||
if err := srv.Shutdown(context.Background()); err != nil {
|
||||
// Error from closing listeners, or context timeout:
|
||||
log.Printf("HTTP server Shutdown: %v", err)
|
||||
}
|
||||
close(idleConnsClosed)
|
||||
}()
|
||||
|
||||
if err := srv.Serve(ln); err != http.ErrServerClosed {
|
||||
// Error starting or closing listener:
|
||||
log.Printf("HTTP server ListenAndServe: %v", err)
|
||||
}
|
||||
|
||||
<-idleConnsClosed
|
||||
|
||||
reqValues := url.Values{}
|
||||
reqValues.Add("grant_type", "authorization_code")
|
||||
reqValues.Add("code", code)
|
||||
reqValues.Add("redirect_uri", redirectURI)
|
||||
reqValues.Add("client_id", clientID)
|
||||
reqValues.Add("me", me.String())
|
||||
|
||||
res, err := http.PostForm(endpoints.TokenEndpoint, reqValues)
|
||||
if err != nil {
|
||||
return tokenResponse, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
dec := json.NewDecoder(res.Body)
|
||||
err = dec.Decode(&tokenResponse)
|
||||
if err != nil {
|
||||
return tokenResponse, err
|
||||
}
|
||||
|
||||
return tokenResponse, nil
|
||||
}
|
||||
|
||||
func CreateAuthenticationURL(authURL url.URL, meURL, clientID, redirectURI, state string) string {
|
||||
q := authURL.Query()
|
||||
|
||||
q.Add("response_type", "id")
|
||||
q.Add("me", meURL)
|
||||
q.Add("client_id", clientID)
|
||||
q.Add("redirect_uri", redirectURI)
|
||||
q.Add("state", state)
|
||||
|
||||
authURL.RawQuery = q.Encode()
|
||||
|
||||
return authURL.String()
|
||||
}
|
||||
|
||||
func CreateAuthorizationURL(authURL url.URL, meURL, clientID, redirectURI, state, scope string) string {
|
||||
q := authURL.Query()
|
||||
q.Add("response_type", "code")
|
||||
q.Add("me", meURL)
|
||||
q.Add("client_id", clientID)
|
||||
q.Add("redirect_uri", redirectURI)
|
||||
q.Add("state", state)
|
||||
q.Add("scope", scope)
|
||||
authURL.RawQuery = q.Encode()
|
||||
return authURL.String()
|
||||
}
|
2
vendor/linkheader/.gitignore
vendored
Normal file
2
vendor/linkheader/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
cpu.out
|
||||
linkheader.test
|
6
vendor/linkheader/.travis.yml
vendored
Normal file
6
vendor/linkheader/.travis.yml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
- 1.7
|
||||
- tip
|
10
vendor/linkheader/CONTRIBUTING.mkd
vendored
Normal file
10
vendor/linkheader/CONTRIBUTING.mkd
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Contributing
|
||||
|
||||
* Raise an issue if appropriate
|
||||
* Fork the repo
|
||||
* Bootstrap the dev dependencies (run `./script/bootstrap`)
|
||||
* Make your changes
|
||||
* Use [gofmt](https://golang.org/cmd/gofmt/)
|
||||
* Make sure the tests pass (run `./script/test`)
|
||||
* Make sure the linters pass (run `./script/lint`)
|
||||
* Issue a pull request
|
21
vendor/linkheader/LICENSE
vendored
Normal file
21
vendor/linkheader/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 Tom Hudson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
35
vendor/linkheader/README.mkd
vendored
Normal file
35
vendor/linkheader/README.mkd
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Golang Link Header Parser
|
||||
|
||||
Library for parsing HTTP Link headers. Requires Go 1.6 or higher.
|
||||
|
||||
Docs can be found on [the GoDoc page](https://godoc.org/github.com/tomnomnom/linkheader).
|
||||
|
||||
[![Build Status](https://travis-ci.org/tomnomnom/linkheader.svg)](https://travis-ci.org/tomnomnom/linkheader)
|
||||
|
||||
## Basic Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tomnomnom/linkheader"
|
||||
)
|
||||
|
||||
func main() {
|
||||
header := "<https://api.github.com/user/58276/repos?page=2>; rel=\"next\"," +
|
||||
"<https://api.github.com/user/58276/repos?page=2>; rel=\"last\""
|
||||
links := linkheader.Parse(header)
|
||||
|
||||
for _, link := range links {
|
||||
fmt.Printf("URL: %s; Rel: %s\n", link.URL, link.Rel)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// URL: https://api.github.com/user/58276/repos?page=2; Rel: next
|
||||
// URL: https://api.github.com/user/58276/repos?page=2; Rel: last
|
||||
```
|
||||
|
||||
|
76
vendor/linkheader/examples_test.go
vendored
Normal file
76
vendor/linkheader/examples_test.go
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package linkheader_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tomnomnom/linkheader"
|
||||
)
|
||||
|
||||
func ExampleParse() {
|
||||
header := "<https://api.github.com/user/58276/repos?page=2>; rel=\"next\"," +
|
||||
"<https://api.github.com/user/58276/repos?page=2>; rel=\"last\""
|
||||
links := linkheader.Parse(header)
|
||||
|
||||
for _, link := range links {
|
||||
fmt.Printf("URL: %s; Rel: %s\n", link.URL, link.Rel)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// URL: https://api.github.com/user/58276/repos?page=2; Rel: next
|
||||
// URL: https://api.github.com/user/58276/repos?page=2; Rel: last
|
||||
}
|
||||
|
||||
func ExampleParseMultiple() {
|
||||
headers := []string{
|
||||
"<https://api.github.com/user/58276/repos?page=2>; rel=\"next\"",
|
||||
"<https://api.github.com/user/58276/repos?page=2>; rel=\"last\"",
|
||||
}
|
||||
links := linkheader.ParseMultiple(headers)
|
||||
|
||||
for _, link := range links {
|
||||
fmt.Printf("URL: %s; Rel: %s\n", link.URL, link.Rel)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// URL: https://api.github.com/user/58276/repos?page=2; Rel: next
|
||||
// URL: https://api.github.com/user/58276/repos?page=2; Rel: last
|
||||
}
|
||||
|
||||
func ExampleLinks_FilterByRel() {
|
||||
header := "<https://api.github.com/user/58276/repos?page=2>; rel=\"next\"," +
|
||||
"<https://api.github.com/user/58276/repos?page=2>; rel=\"last\""
|
||||
links := linkheader.Parse(header)
|
||||
|
||||
for _, link := range links.FilterByRel("last") {
|
||||
fmt.Printf("URL: %s; Rel: %s\n", link.URL, link.Rel)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// URL: https://api.github.com/user/58276/repos?page=2; Rel: last
|
||||
|
||||
}
|
||||
|
||||
func ExampleLink_String() {
|
||||
link := linkheader.Link{
|
||||
URL: "http://example.com/page/2",
|
||||
Rel: "next",
|
||||
}
|
||||
|
||||
fmt.Printf("Link: %s\n", link.String())
|
||||
|
||||
// Output:
|
||||
// Link: <http://example.com/page/2>; rel="next"
|
||||
}
|
||||
|
||||
func ExampleLinks_String() {
|
||||
|
||||
links := linkheader.Links{
|
||||
{URL: "http://example.com/page/3", Rel: "next"},
|
||||
{URL: "http://example.com/page/1", Rel: "last"},
|
||||
}
|
||||
|
||||
fmt.Printf("Link: %s\n", links.String())
|
||||
|
||||
// Output:
|
||||
// Link: <http://example.com/page/3>; rel="next", <http://example.com/page/1>; rel="last"
|
||||
}
|
148
vendor/linkheader/main.go
vendored
Normal file
148
vendor/linkheader/main.go
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Package linkheader provides functions for parsing HTTP Link headers
|
||||
package linkheader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Link is a single URL and related parameters
|
||||
type Link struct {
|
||||
URL string
|
||||
Rel string
|
||||
Params map[string]string
|
||||
}
|
||||
|
||||
// HasParam returns if a Link has a particular parameter or not
|
||||
func (l Link) HasParam(key string) bool {
|
||||
for p := range l.Params {
|
||||
if p == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Param returns the value of a parameter if it exists
|
||||
func (l Link) Param(key string) string {
|
||||
for k, v := range l.Params {
|
||||
if key == k {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// String returns the string representation of a link
|
||||
func (l Link) String() string {
|
||||
|
||||
p := make([]string, 0, len(l.Params))
|
||||
for k, v := range l.Params {
|
||||
p = append(p, fmt.Sprintf("%s=\"%s\"", k, v))
|
||||
}
|
||||
if l.Rel != "" {
|
||||
p = append(p, fmt.Sprintf("%s=\"%s\"", "rel", l.Rel))
|
||||
}
|
||||
return fmt.Sprintf("<%s>; %s", l.URL, strings.Join(p, "; "))
|
||||
}
|
||||
|
||||
// Links is a slice of Link structs
|
||||
type Links []Link
|
||||
|
||||
// FilterByRel filters a group of Links by the provided Rel attribute
|
||||
func (l Links) FilterByRel(r string) Links {
|
||||
links := make(Links, 0)
|
||||
for _, link := range l {
|
||||
if link.Rel == r {
|
||||
links = append(links, link)
|
||||
}
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
// String returns the string representation of multiple Links
|
||||
// for use in HTTP responses etc
|
||||
func (l Links) String() string {
|
||||
if l == nil {
|
||||
return fmt.Sprint(nil)
|
||||
}
|
||||
|
||||
var strs []string
|
||||
for _, link := range l {
|
||||
strs = append(strs, link.String())
|
||||
}
|
||||
return strings.Join(strs, ", ")
|
||||
}
|
||||
|
||||
// Parse parses a raw Link header in the form:
|
||||
// <url>; rel="foo", <url>; rel="bar"; wat="dis"
|
||||
// returning a slice of Link structs
|
||||
func Parse(raw string) Links {
|
||||
var links Links
|
||||
|
||||
// One chunk: <url>; rel="foo"
|
||||
for _, chunk := range strings.Split(raw, ",") {
|
||||
|
||||
link := Link{URL: "", Rel: "", Params: make(map[string]string)}
|
||||
|
||||
// Figure out what each piece of the chunk is
|
||||
for _, piece := range strings.Split(chunk, ";") {
|
||||
|
||||
piece = strings.Trim(piece, " ")
|
||||
if piece == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// URL
|
||||
if piece[0] == '<' && piece[len(piece)-1] == '>' {
|
||||
link.URL = strings.Trim(piece, "<>")
|
||||
continue
|
||||
}
|
||||
|
||||
// Params
|
||||
key, val := parseParam(piece)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Special case for rel
|
||||
if strings.ToLower(key) == "rel" {
|
||||
link.Rel = val
|
||||
} else {
|
||||
link.Params[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
if link.URL != "" {
|
||||
links = append(links, link)
|
||||
}
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
// ParseMultiple is like Parse, but accepts a slice of headers
|
||||
// rather than just one header string
|
||||
func ParseMultiple(headers []string) Links {
|
||||
links := make(Links, 0)
|
||||
for _, header := range headers {
|
||||
links = append(links, Parse(header)...)
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
// parseParam takes a raw param in the form key="val" and
|
||||
// returns the key and value as seperate strings
|
||||
func parseParam(raw string) (key, val string) {
|
||||
|
||||
parts := strings.SplitN(raw, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
key = parts[0]
|
||||
val = strings.Trim(parts[1], "\"")
|
||||
|
||||
return key, val
|
||||
|
||||
}
|
173
vendor/linkheader/main_test.go
vendored
Normal file
173
vendor/linkheader/main_test.go
vendored
Normal file
|
@ -0,0 +1,173 @@
|
|||
package linkheader
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSimple(t *testing.T) {
|
||||
// Test case stolen from https://github.com/thlorenz/parse-link-header :)
|
||||
header := "<https://api.github.com/user/9287/repos?page=3&per_page=100>; rel=\"next\", " +
|
||||
"<https://api.github.com/user/9287/repos?page=1&per_page=100>; rel=\"prev\"; pet=\"cat\", " +
|
||||
"<https://api.github.com/user/9287/repos?page=5&per_page=100>; rel=\"last\""
|
||||
|
||||
links := Parse(header)
|
||||
|
||||
if len(links) != 3 {
|
||||
t.Errorf("Should have been 3 links returned, got %d", len(links))
|
||||
}
|
||||
|
||||
if links[0].URL != "https://api.github.com/user/9287/repos?page=3&per_page=100" {
|
||||
t.Errorf("First link should have URL 'https://api.github.com/user/9287/repos?page=3&per_page=100'")
|
||||
}
|
||||
|
||||
if links[0].Rel != "next" {
|
||||
t.Errorf("First link should have rel=\"next\"")
|
||||
}
|
||||
|
||||
if len(links[0].Params) != 0 {
|
||||
t.Errorf("First link should have exactly 0 params, but has %d", len(links[0].Params))
|
||||
}
|
||||
|
||||
if len(links[1].Params) != 1 {
|
||||
t.Errorf("Second link should have exactly 1 params, but has %d", len(links[1].Params))
|
||||
}
|
||||
|
||||
if links[1].Params["pet"] != "cat" {
|
||||
t.Errorf("Second link's 'pet' param should be 'cat', but was %s", links[1].Params["pet"])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
links := Parse("")
|
||||
if links != nil {
|
||||
t.Errorf("Return value should be nil, but was %s", len(links))
|
||||
}
|
||||
}
|
||||
|
||||
// Although not often seen in the wild, the grammar in RFC 5988 suggests that it's
|
||||
// valid for a link header to have nothing but a URL.
|
||||
func TestNoRel(t *testing.T) {
|
||||
links := Parse("<http://example.com>")
|
||||
|
||||
if len(links) != 1 {
|
||||
t.Fatalf("Length of links should be 1, but was %d", len(links))
|
||||
}
|
||||
|
||||
if links[0].URL != "http://example.com" {
|
||||
t.Errorf("URL should be http://example.com, but was %s", links[0].URL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkMethods(t *testing.T) {
|
||||
header := "<https://api.github.com/user/9287/repos?page=1&per_page=100>; rel=\"prev\"; pet=\"cat\""
|
||||
links := Parse(header)
|
||||
link := links[0]
|
||||
|
||||
if link.HasParam("foo") {
|
||||
t.Errorf("Link should not have param 'foo'")
|
||||
}
|
||||
|
||||
val := link.Param("pet")
|
||||
if val != "cat" {
|
||||
t.Errorf("Link should have param pet=\"cat\"")
|
||||
}
|
||||
|
||||
val = link.Param("foo")
|
||||
if val != "" {
|
||||
t.Errorf("Link should not have value for param 'foo'")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLinksMethods(t *testing.T) {
|
||||
header := "<https://api.github.com/user/9287/repos?page=3&per_page=100>; rel=\"next\", " +
|
||||
"<https://api.github.com/user/9287/repos?page=1&per_page=100>; rel=\"stylesheet\"; pet=\"cat\", " +
|
||||
"<https://api.github.com/user/9287/repos?page=5&per_page=100>; rel=\"stylesheet\""
|
||||
|
||||
links := Parse(header)
|
||||
|
||||
filtered := links.FilterByRel("next")
|
||||
|
||||
if filtered[0].URL != "https://api.github.com/user/9287/repos?page=3&per_page=100" {
|
||||
t.Errorf("URL did not match expected")
|
||||
}
|
||||
|
||||
filtered = links.FilterByRel("stylesheet")
|
||||
if len(filtered) != 2 {
|
||||
t.Errorf("Filter for stylesheet should yield 2 results but got %d", len(filtered))
|
||||
}
|
||||
|
||||
filtered = links.FilterByRel("notarel")
|
||||
if len(filtered) != 0 {
|
||||
t.Errorf("Filter by non-existant rel should yeild no results")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestParseMultiple(t *testing.T) {
|
||||
headers := []string{
|
||||
"<https://api.github.com/user/58276/repos?page=2>; rel=\"next\"",
|
||||
"<https://api.github.com/user/58276/repos?page=2>; rel=\"last\"",
|
||||
}
|
||||
|
||||
links := ParseMultiple(headers)
|
||||
|
||||
if len(links) != 2 {
|
||||
t.Errorf("Should have returned 2 links")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkToString(t *testing.T) {
|
||||
l := Link{
|
||||
URL: "http://example.com/page/2",
|
||||
Rel: "next",
|
||||
Params: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
|
||||
have := l.String()
|
||||
|
||||
parsed := Parse(have)
|
||||
|
||||
if len(parsed) != 1 {
|
||||
t.Errorf("Expected only 1 link")
|
||||
}
|
||||
|
||||
if parsed[0].URL != l.URL {
|
||||
t.Errorf("Re-parsed link header should have matching URL, but has `%s`", parsed[0].URL)
|
||||
}
|
||||
|
||||
if parsed[0].Rel != l.Rel {
|
||||
t.Errorf("Re-parsed link header should have matching rel, but has `%s`", parsed[0].Rel)
|
||||
}
|
||||
|
||||
if parsed[0].Param("foo") != "bar" {
|
||||
t.Errorf("Re-parsed link header should have foo=\"bar\" but doesn't")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinksToString(t *testing.T) {
|
||||
ls := Links{
|
||||
{URL: "http://example.com/page/3", Rel: "next"},
|
||||
{URL: "http://example.com/page/1", Rel: "last"},
|
||||
}
|
||||
|
||||
have := ls.String()
|
||||
|
||||
want := "<http://example.com/page/3>; rel=\"next\", <http://example.com/page/1>; rel=\"last\""
|
||||
|
||||
if have != want {
|
||||
t.Errorf("Want `%s`, have `%s`", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParse(b *testing.B) {
|
||||
|
||||
header := "<https://api.github.com/user/9287/repos?page=3&per_page=100>; rel=\"next\", " +
|
||||
"<https://api.github.com/user/9287/repos?page=1&per_page=100>; rel=\"prev\"; pet=\"cat\", " +
|
||||
"<https://api.github.com/user/9287/repos?page=5&per_page=100>; rel=\"last\""
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = Parse(header)
|
||||
}
|
||||
}
|
6
vendor/linkheader/script/bootstrap
vendored
Executable file
6
vendor/linkheader/script/bootstrap
vendored
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
PROJDIR=$(cd `dirname $0`/.. && pwd)
|
||||
|
||||
echo "Installing gometalinter and linters..."
|
||||
go get github.com/alecthomas/gometalinter
|
||||
gometalinter --install
|
6
vendor/linkheader/script/lint
vendored
Executable file
6
vendor/linkheader/script/lint
vendored
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
PROJDIR=$(cd `dirname $0`/.. && pwd)
|
||||
|
||||
cd ${PROJDIR}
|
||||
go get
|
||||
gometalinter
|
7
vendor/linkheader/script/profile
vendored
Executable file
7
vendor/linkheader/script/profile
vendored
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
PROJDIR=$(cd `dirname $0`/.. && pwd)
|
||||
cd ${PROJDIR}
|
||||
|
||||
go test -bench . -benchmem -cpuprofile cpu.out
|
||||
go tool pprof linkheader.test cpu.out
|
3
vendor/linkheader/script/test
vendored
Executable file
3
vendor/linkheader/script/test
vendored
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
PROJDIR=$(cd `dirname $0`/.. && pwd)
|
||||
cd $PROJDIR && go test
|
Loading…
Reference in New Issue
Block a user