replace zxq.co/ripple/hanayo

This commit is contained in:
Alicia
2019-02-23 13:29:15 +00:00
commit c3d206c173
5871 changed files with 1353715 additions and 0 deletions

8
vendor/github.com/DataDog/datadog-go/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,8 @@
language: go
go:
- 1.4
- 1.5
script:
- go test -v ./...

54
vendor/github.com/DataDog/datadog-go/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,54 @@
Changes
=======
# 1.1.0 / Unreleased
### Notes
* [FEATURE] Export serviceCheckStatus allowing interfaces to statsd.Client. See [#19][] (Thanks [@Jasrags][])
* [FEATURE] Client.sendMsg(). Check payload length for all messages. See [#25][] (Thanks [@theckman][])
* [BUGFIX] Remove new lines from tags. See [#21][] (Thanks [@sjung-stripe][])
* [BUGFIX] Do not panic on Client.Event when `nil`. See [#28][]
* [DOCUMENTATION] Update `decr` documentation to match implementation. See [#30][] (Thanks [@kcollasarundell][])
# 1.0.0 / 2016-08-22
### Details
We hadn't been properly versioning this project. We will begin to do so with this
`1.0.0` release. We had some contributions in the past and would like to thank the
contributors [@aviau][], [@sschepens][], [@jovanbrakus][], [@abtris][], [@tummychow][], [@gphat][], [@diasjorge][],
[@victortrac][], [@seiffert][] and [@w-vi][], in no particular order, for their work.
Below, for reference, the latest improvements made in 07/2016 - 08/2016
### Notes
* [FEATURE] Implemented support for service checks. See [#17][] and [#5][]. (Thanks [@jovanbrakus][] and [@diasjorge][]).
* [FEATURE] Add Incr, Decr, Timing and more docs.. See [#15][]. (Thanks [@gphat][])
* [BUGFIX] Do not append to shared slice. See [#16][]. (Thanks [@tummychow][])
<!--- The following link definition list is generated by PimpMyChangelog --->
[#5]: https://github.com/DataDog/datadog-go/issues/5
[#15]: https://github.com/DataDog/datadog-go/issues/15
[#16]: https://github.com/DataDog/datadog-go/issues/16
[#17]: https://github.com/DataDog/datadog-go/issues/17
[#19]: https://github.com/DataDog/datadog-go/issues/19
[#21]: https://github.com/DataDog/datadog-go/issues/21
[#25]: https://github.com/DataDog/datadog-go/issues/25
[#28]: https://github.com/DataDog/datadog-go/issues/28
[#30]: https://github.com/DataDog/datadog-go/issues/30
[@Jasrags]: https://github.com/Jasrags
[@abtris]: https://github.com/abtris
[@aviau]: https://github.com/aviau
[@diasjorge]: https://github.com/diasjorge
[@gphat]: https://github.com/gphat
[@jovanbrakus]: https://github.com/jovanbrakus
[@kcollasarundell]: https://github.com/kcollasarundell
[@seiffert]: https://github.com/seiffert
[@sjung-stripe]: https://github.com/sjung-stripe
[@sschepens]: https://github.com/sschepens
[@theckman]: https://github.com/theckman
[@tummychow]: https://github.com/tummychow
[@victortrac]: https://github.com/victortrac
[@w-vi]: https://github.com/w-vi

19
vendor/github.com/DataDog/datadog-go/LICENSE.txt generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2015 Datadog, Inc
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.

33
vendor/github.com/DataDog/datadog-go/README.md generated vendored Normal file
View File

@@ -0,0 +1,33 @@
[![Build Status](https://travis-ci.org/DataDog/datadog-go.svg?branch=master)](https://travis-ci.org/DataDog/datadog-go)
# Overview
Packages in `datadog-go` provide Go clients for various APIs at [DataDog](http://datadoghq.com).
## Statsd
[![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/DataDog/datadog-go/statsd)
[![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](http://opensource.org/licenses/MIT)
The [statsd](https://github.com/DataDog/datadog-go/tree/master/statsd) package provides a client for
[dogstatsd](http://docs.datadoghq.com/guides/dogstatsd/):
```go
import "github.com/DataDog/datadog-go/statsd"
func main() {
c, err := statsd.New("127.0.0.1:8125")
if err != nil {
log.Fatal(err)
}
// prefix every metric with the app name
c.Namespace = "flubber."
// send the EC2 availability zone as a tag with every metric
c.Tags = append(c.Tags, "us-east-1a")
err = c.Gauge("request.duration", 1.2, nil, 1)
// ...
}
```
## License
All code distributed under the [MIT License](http://opensource.org/licenses/MIT) unless otherwise specified.

52
vendor/github.com/DataDog/datadog-go/statsd/README.md generated vendored Normal file
View File

@@ -0,0 +1,52 @@
## Overview
Package `statsd` provides a Go [dogstatsd](http://docs.datadoghq.com/guides/dogstatsd/) client. Dogstatsd extends Statsd, adding tags
and histograms.
## Get the code
$ go get github.com/DataDog/datadog-go/statsd
## Usage
```go
// Create the client
c, err := statsd.New("127.0.0.1:8125")
if err != nil {
log.Fatal(err)
}
// Prefix every metric with the app name
c.Namespace = "flubber."
// Send the EC2 availability zone as a tag with every metric
c.Tags = append(c.Tags, "us-east-1a")
// Do some metrics!
err = c.Gauge("request.queue_depth", 12, nil, 1)
err = c.Timing("request.duration", duration, nil, 1) // Uses a time.Duration!
err = c.TimeInMilliseconds("request", 12, nil, 1)
err = c.Incr("request.count_total", nil, 1)
err = c.Decr("request.count_total", nil, 1)
err = c.Count("request.count_total", 2, nil, 1)
```
## Buffering Client
DogStatsD accepts packets with multiple statsd payloads in them. Using the BufferingClient via `NewBufferingClient` will buffer up commands and send them when the buffer is reached or after 100msec.
## Development
Run the tests with:
$ go test
## Documentation
Please see: http://godoc.org/github.com/DataDog/datadog-go/statsd
## License
go-dogstatsd is released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).
## Credits
Original code by [ooyala](https://github.com/ooyala/go-dogstatsd).

580
vendor/github.com/DataDog/datadog-go/statsd/statsd.go generated vendored Normal file
View File

@@ -0,0 +1,580 @@
// Copyright 2013 Ooyala, Inc.
/*
Package statsd provides a Go dogstatsd client. Dogstatsd extends the popular statsd,
adding tags and histograms and pushing upstream to Datadog.
Refer to http://docs.datadoghq.com/guides/dogstatsd/ for information about DogStatsD.
Example Usage:
// Create the client
c, err := statsd.New("127.0.0.1:8125")
if err != nil {
log.Fatal(err)
}
// Prefix every metric with the app name
c.Namespace = "flubber."
// Send the EC2 availability zone as a tag with every metric
c.Tags = append(c.Tags, "us-east-1a")
err = c.Gauge("request.duration", 1.2, nil, 1)
statsd is based on go-statsd-client.
*/
package statsd
import (
"bytes"
"errors"
"fmt"
"io"
"math/rand"
"net"
"strconv"
"strings"
"sync"
"time"
)
/*
OptimalPayloadSize defines the optimal payload size for a UDP datagram, 1432 bytes
is optimal for regular networks with an MTU of 1500 so datagrams don't get
fragmented. It's generally recommended not to fragment UDP datagrams as losing
a single fragment will cause the entire datagram to be lost.
This can be increased if your network has a greater MTU or you don't mind UDP
datagrams getting fragmented. The practical limit is MaxUDPPayloadSize
*/
const OptimalPayloadSize = 1432
/*
MaxUDPPayloadSize defines the maximum payload size for a UDP datagram.
Its value comes from the calculation: 65535 bytes Max UDP datagram size -
8byte UDP header - 60byte max IP headers
any number greater than that will see frames being cut out.
*/
const MaxUDPPayloadSize = 65467
// A Client is a handle for sending udp messages to dogstatsd. It is safe to
// use one Client from multiple goroutines simultaneously.
type Client struct {
conn net.Conn
// Namespace to prepend to all statsd calls
Namespace string
// Tags are global tags to be added to every statsd call
Tags []string
// BufferLength is the length of the buffer in commands.
bufferLength int
flushTime time.Duration
commands []string
buffer bytes.Buffer
stop bool
sync.Mutex
}
// New returns a pointer to a new Client given an addr in the format "hostname:port".
func New(addr string) (*Client, error) {
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
conn, err := net.DialUDP("udp", nil, udpAddr)
if err != nil {
return nil, err
}
client := &Client{conn: conn}
return client, nil
}
// NewBuffered returns a Client that buffers its output and sends it in chunks.
// Buflen is the length of the buffer in number of commands.
func NewBuffered(addr string, buflen int) (*Client, error) {
client, err := New(addr)
if err != nil {
return nil, err
}
client.bufferLength = buflen
client.commands = make([]string, 0, buflen)
client.flushTime = time.Millisecond * 100
go client.watch()
return client, nil
}
// format a message from its name, value, tags and rate. Also adds global
// namespace and tags.
func (c *Client) format(name, value string, tags []string, rate float64) string {
var buf bytes.Buffer
if c.Namespace != "" {
buf.WriteString(c.Namespace)
}
buf.WriteString(name)
buf.WriteString(":")
buf.WriteString(value)
if rate < 1 {
buf.WriteString(`|@`)
buf.WriteString(strconv.FormatFloat(rate, 'f', -1, 64))
}
writeTagString(&buf, c.Tags, tags)
return buf.String()
}
func (c *Client) watch() {
for _ = range time.Tick(c.flushTime) {
if c.stop {
return
}
c.Lock()
if len(c.commands) > 0 {
// FIXME: eating error here
c.flush()
}
c.Unlock()
}
}
func (c *Client) append(cmd string) error {
c.Lock()
defer c.Unlock()
c.commands = append(c.commands, cmd)
// if we should flush, lets do it
if len(c.commands) == c.bufferLength {
if err := c.flush(); err != nil {
return err
}
}
return nil
}
func (c *Client) joinMaxSize(cmds []string, sep string, maxSize int) ([][]byte, []int) {
c.buffer.Reset() //clear buffer
var frames [][]byte
var ncmds []int
sepBytes := []byte(sep)
sepLen := len(sep)
elem := 0
for _, cmd := range cmds {
needed := len(cmd)
if elem != 0 {
needed = needed + sepLen
}
if c.buffer.Len()+needed <= maxSize {
if elem != 0 {
c.buffer.Write(sepBytes)
}
c.buffer.WriteString(cmd)
elem++
} else {
frames = append(frames, copyAndResetBuffer(&c.buffer))
ncmds = append(ncmds, elem)
// if cmd is bigger than maxSize it will get flushed on next loop
c.buffer.WriteString(cmd)
elem = 1
}
}
//add whatever is left! if there's actually something
if c.buffer.Len() > 0 {
frames = append(frames, copyAndResetBuffer(&c.buffer))
ncmds = append(ncmds, elem)
}
return frames, ncmds
}
func copyAndResetBuffer(buf *bytes.Buffer) []byte {
tmpBuf := make([]byte, buf.Len())
copy(tmpBuf, buf.Bytes())
buf.Reset()
return tmpBuf
}
// flush the commands in the buffer. Lock must be held by caller.
func (c *Client) flush() error {
frames, flushable := c.joinMaxSize(c.commands, "\n", OptimalPayloadSize)
var err error
cmdsFlushed := 0
for i, data := range frames {
_, e := c.conn.Write(data)
if e != nil {
err = e
break
}
cmdsFlushed += flushable[i]
}
// clear the slice with a slice op, doesn't realloc
if cmdsFlushed == len(c.commands) {
c.commands = c.commands[:0]
} else {
//this case will cause a future realloc...
// drop problematic command though (sorry).
c.commands = c.commands[cmdsFlushed+1:]
}
return err
}
func (c *Client) sendMsg(msg string) error {
// return an error if message is bigger than MaxUDPPayloadSize
if len(msg) > MaxUDPPayloadSize {
return errors.New("message size exceeds MaxUDPPayloadSize")
}
// if this client is buffered, then we'll just append this
if c.bufferLength > 0 {
return c.append(msg)
}
_, err := c.conn.Write([]byte(msg))
return err
}
// send handles sampling and sends the message over UDP. It also adds global namespace prefixes and tags.
func (c *Client) send(name, value string, tags []string, rate float64) error {
if c == nil {
return nil
}
if rate < 1 && rand.Float64() > rate {
return nil
}
data := c.format(name, value, tags, rate)
return c.sendMsg(data)
}
// Gauge measures the value of a metric at a particular time.
func (c *Client) Gauge(name string, value float64, tags []string, rate float64) error {
stat := fmt.Sprintf("%f|g", value)
return c.send(name, stat, tags, rate)
}
// Count tracks how many times something happened per second.
func (c *Client) Count(name string, value int64, tags []string, rate float64) error {
stat := fmt.Sprintf("%d|c", value)
return c.send(name, stat, tags, rate)
}
// Histogram tracks the statistical distribution of a set of values.
func (c *Client) Histogram(name string, value float64, tags []string, rate float64) error {
stat := fmt.Sprintf("%f|h", value)
return c.send(name, stat, tags, rate)
}
// Decr is just Count of -1
func (c *Client) Decr(name string, tags []string, rate float64) error {
return c.send(name, "-1|c", tags, rate)
}
// Incr is just Count of 1
func (c *Client) Incr(name string, tags []string, rate float64) error {
return c.send(name, "1|c", tags, rate)
}
// Set counts the number of unique elements in a group.
func (c *Client) Set(name string, value string, tags []string, rate float64) error {
stat := fmt.Sprintf("%s|s", value)
return c.send(name, stat, tags, rate)
}
// Timing sends timing information, it is an alias for TimeInMilliseconds
func (c *Client) Timing(name string, value time.Duration, tags []string, rate float64) error {
return c.TimeInMilliseconds(name, value.Seconds()*1000, tags, rate)
}
// TimeInMilliseconds sends timing information in milliseconds.
// It is flushed by statsd with percentiles, mean and other info (https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing)
func (c *Client) TimeInMilliseconds(name string, value float64, tags []string, rate float64) error {
stat := fmt.Sprintf("%f|ms", value)
return c.send(name, stat, tags, rate)
}
// Event sends the provided Event.
func (c *Client) Event(e *Event) error {
if c == nil {
return nil
}
stat, err := e.Encode(c.Tags...)
if err != nil {
return err
}
return c.sendMsg(stat)
}
// SimpleEvent sends an event with the provided title and text.
func (c *Client) SimpleEvent(title, text string) error {
e := NewEvent(title, text)
return c.Event(e)
}
// ServiceCheck sends the provided ServiceCheck.
func (c *Client) ServiceCheck(sc *ServiceCheck) error {
stat, err := sc.Encode(c.Tags...)
if err != nil {
return err
}
return c.sendMsg(stat)
}
// SimpleServiceCheck sends an serviceCheck with the provided name and status.
func (c *Client) SimpleServiceCheck(name string, status ServiceCheckStatus) error {
sc := NewServiceCheck(name, status)
return c.ServiceCheck(sc)
}
// Close the client connection.
func (c *Client) Close() error {
if c == nil {
return nil
}
c.stop = true
return c.conn.Close()
}
// Events support
type eventAlertType string
const (
// Info is the "info" AlertType for events
Info eventAlertType = "info"
// Error is the "error" AlertType for events
Error eventAlertType = "error"
// Warning is the "warning" AlertType for events
Warning eventAlertType = "warning"
// Success is the "success" AlertType for events
Success eventAlertType = "success"
)
type eventPriority string
const (
// Normal is the "normal" Priority for events
Normal eventPriority = "normal"
// Low is the "low" Priority for events
Low eventPriority = "low"
)
// An Event is an object that can be posted to your DataDog event stream.
type Event struct {
// Title of the event. Required.
Title string
// Text is the description of the event. Required.
Text string
// Timestamp is a timestamp for the event. If not provided, the dogstatsd
// server will set this to the current time.
Timestamp time.Time
// Hostname for the event.
Hostname string
// AggregationKey groups this event with others of the same key.
AggregationKey string
// Priority of the event. Can be statsd.Low or statsd.Normal.
Priority eventPriority
// SourceTypeName is a source type for the event.
SourceTypeName string
// AlertType can be statsd.Info, statsd.Error, statsd.Warning, or statsd.Success.
// If absent, the default value applied by the dogstatsd server is Info.
AlertType eventAlertType
// Tags for the event.
Tags []string
}
// NewEvent creates a new event with the given title and text. Error checking
// against these values is done at send-time, or upon running e.Check.
func NewEvent(title, text string) *Event {
return &Event{
Title: title,
Text: text,
}
}
// Check verifies that an event is valid.
func (e Event) Check() error {
if len(e.Title) == 0 {
return fmt.Errorf("statsd.Event title is required")
}
if len(e.Text) == 0 {
return fmt.Errorf("statsd.Event text is required")
}
return nil
}
// Encode returns the dogstatsd wire protocol representation for an event.
// Tags may be passed which will be added to the encoded output but not to
// the Event's list of tags, eg. for default tags.
func (e Event) Encode(tags ...string) (string, error) {
err := e.Check()
if err != nil {
return "", err
}
text := e.escapedText()
var buffer bytes.Buffer
buffer.WriteString("_e{")
buffer.WriteString(strconv.FormatInt(int64(len(e.Title)), 10))
buffer.WriteRune(',')
buffer.WriteString(strconv.FormatInt(int64(len(text)), 10))
buffer.WriteString("}:")
buffer.WriteString(e.Title)
buffer.WriteRune('|')
buffer.WriteString(text)
if !e.Timestamp.IsZero() {
buffer.WriteString("|d:")
buffer.WriteString(strconv.FormatInt(int64(e.Timestamp.Unix()), 10))
}
if len(e.Hostname) != 0 {
buffer.WriteString("|h:")
buffer.WriteString(e.Hostname)
}
if len(e.AggregationKey) != 0 {
buffer.WriteString("|k:")
buffer.WriteString(e.AggregationKey)
}
if len(e.Priority) != 0 {
buffer.WriteString("|p:")
buffer.WriteString(string(e.Priority))
}
if len(e.SourceTypeName) != 0 {
buffer.WriteString("|s:")
buffer.WriteString(e.SourceTypeName)
}
if len(e.AlertType) != 0 {
buffer.WriteString("|t:")
buffer.WriteString(string(e.AlertType))
}
writeTagString(&buffer, tags, e.Tags)
return buffer.String(), nil
}
// ServiceCheck support
type ServiceCheckStatus byte
const (
// Ok is the "ok" ServiceCheck status
Ok ServiceCheckStatus = 0
// Warn is the "warning" ServiceCheck status
Warn ServiceCheckStatus = 1
// Critical is the "critical" ServiceCheck status
Critical ServiceCheckStatus = 2
// Unknown is the "unknown" ServiceCheck status
Unknown ServiceCheckStatus = 3
)
// An ServiceCheck is an object that contains status of DataDog service check.
type ServiceCheck struct {
// Name of the service check. Required.
Name string
// Status of service check. Required.
Status ServiceCheckStatus
// Timestamp is a timestamp for the serviceCheck. If not provided, the dogstatsd
// server will set this to the current time.
Timestamp time.Time
// Hostname for the serviceCheck.
Hostname string
// A message describing the current state of the serviceCheck.
Message string
// Tags for the serviceCheck.
Tags []string
}
// NewServiceCheck creates a new serviceCheck with the given name and status. Error checking
// against these values is done at send-time, or upon running sc.Check.
func NewServiceCheck(name string, status ServiceCheckStatus) *ServiceCheck {
return &ServiceCheck{
Name: name,
Status: status,
}
}
// Check verifies that an event is valid.
func (sc ServiceCheck) Check() error {
if len(sc.Name) == 0 {
return fmt.Errorf("statsd.ServiceCheck name is required")
}
if byte(sc.Status) < 0 || byte(sc.Status) > 3 {
return fmt.Errorf("statsd.ServiceCheck status has invalid value")
}
return nil
}
// Encode returns the dogstatsd wire protocol representation for an serviceCheck.
// Tags may be passed which will be added to the encoded output but not to
// the Event's list of tags, eg. for default tags.
func (sc ServiceCheck) Encode(tags ...string) (string, error) {
err := sc.Check()
if err != nil {
return "", err
}
message := sc.escapedMessage()
var buffer bytes.Buffer
buffer.WriteString("_sc|")
buffer.WriteString(sc.Name)
buffer.WriteRune('|')
buffer.WriteString(strconv.FormatInt(int64(sc.Status), 10))
if !sc.Timestamp.IsZero() {
buffer.WriteString("|d:")
buffer.WriteString(strconv.FormatInt(int64(sc.Timestamp.Unix()), 10))
}
if len(sc.Hostname) != 0 {
buffer.WriteString("|h:")
buffer.WriteString(sc.Hostname)
}
writeTagString(&buffer, tags, sc.Tags)
if len(message) != 0 {
buffer.WriteString("|m:")
buffer.WriteString(message)
}
return buffer.String(), nil
}
func (e Event) escapedText() string {
return strings.Replace(e.Text, "\n", "\\n", -1)
}
func (sc ServiceCheck) escapedMessage() string {
msg := strings.Replace(sc.Message, "\n", "\\n", -1)
return strings.Replace(msg, "m:", `m\:`, -1)
}
func removeNewlines(str string) string {
return strings.Replace(str, "\n", "", -1)
}
func writeTagString(w io.Writer, tagList1, tagList2 []string) {
// the tag lists may be shared with other callers, so we cannot modify
// them in any way (which means we cannot append to them either)
// therefore we must make an entirely separate copy just for this call
totalLen := len(tagList1) + len(tagList2)
if totalLen == 0 {
return
}
tags := make([]string, 0, totalLen)
tags = append(tags, tagList1...)
tags = append(tags, tagList2...)
io.WriteString(w, "|#")
io.WriteString(w, removeNewlines(tags[0]))
for _, tag := range tags[1:] {
io.WriteString(w, ",")
io.WriteString(w, removeNewlines(tag))
}
}

View File

@@ -0,0 +1,620 @@
// Copyright 2013 Ooyala, Inc.
package statsd
import (
"fmt"
"io"
"net"
"reflect"
"strings"
"testing"
"time"
)
var dogstatsdTests = []struct {
GlobalNamespace string
GlobalTags []string
Method string
Metric string
Value interface{}
Tags []string
Rate float64
Expected string
}{
{"", nil, "Gauge", "test.gauge", 1.0, nil, 1.0, "test.gauge:1.000000|g"},
{"", nil, "Gauge", "test.gauge", 1.0, nil, 0.999999, "test.gauge:1.000000|g|@0.999999"},
{"", nil, "Gauge", "test.gauge", 1.0, []string{"tagA"}, 1.0, "test.gauge:1.000000|g|#tagA"},
{"", nil, "Gauge", "test.gauge", 1.0, []string{"tagA", "tagB"}, 1.0, "test.gauge:1.000000|g|#tagA,tagB"},
{"", nil, "Gauge", "test.gauge", 1.0, []string{"tagA"}, 0.999999, "test.gauge:1.000000|g|@0.999999|#tagA"},
{"", nil, "Count", "test.count", int64(1), []string{"tagA"}, 1.0, "test.count:1|c|#tagA"},
{"", nil, "Count", "test.count", int64(-1), []string{"tagA"}, 1.0, "test.count:-1|c|#tagA"},
{"", nil, "Histogram", "test.histogram", 2.3, []string{"tagA"}, 1.0, "test.histogram:2.300000|h|#tagA"},
{"", nil, "Set", "test.set", "uuid", []string{"tagA"}, 1.0, "test.set:uuid|s|#tagA"},
{"flubber.", nil, "Set", "test.set", "uuid", []string{"tagA"}, 1.0, "flubber.test.set:uuid|s|#tagA"},
{"", []string{"tagC"}, "Set", "test.set", "uuid", []string{"tagA"}, 1.0, "test.set:uuid|s|#tagC,tagA"},
{"", nil, "Count", "test.count", int64(1), []string{"hello\nworld"}, 1.0, "test.count:1|c|#helloworld"},
}
func assertNotPanics(t *testing.T, f func()) {
defer func() {
if r := recover(); r != nil {
t.Fatal(r)
}
}()
f()
}
func TestClient(t *testing.T) {
addr := "localhost:1201"
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
t.Fatal(err)
}
server, err := net.ListenUDP("udp", udpAddr)
if err != nil {
t.Fatal(err)
}
defer server.Close()
client, err := New(addr)
if err != nil {
t.Fatal(err)
}
for _, tt := range dogstatsdTests {
client.Namespace = tt.GlobalNamespace
client.Tags = tt.GlobalTags
method := reflect.ValueOf(client).MethodByName(tt.Method)
e := method.Call([]reflect.Value{
reflect.ValueOf(tt.Metric),
reflect.ValueOf(tt.Value),
reflect.ValueOf(tt.Tags),
reflect.ValueOf(tt.Rate)})[0]
errInter := e.Interface()
if errInter != nil {
t.Fatal(errInter.(error))
}
bytes := make([]byte, 1024)
n, err := server.Read(bytes)
if err != nil {
t.Fatal(err)
}
message := bytes[:n]
if string(message) != tt.Expected {
t.Errorf("Expected: %s. Actual: %s", tt.Expected, string(message))
}
}
}
func TestBufferedClient(t *testing.T) {
addr := "localhost:1201"
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
t.Fatal(err)
}
server, err := net.ListenUDP("udp", udpAddr)
if err != nil {
t.Fatal(err)
}
defer server.Close()
conn, err := net.DialUDP("udp", nil, udpAddr)
if err != nil {
t.Fatal(err)
}
bufferLength := 8
client := &Client{
conn: conn,
commands: make([]string, 0, bufferLength),
bufferLength: bufferLength,
}
client.Namespace = "foo."
client.Tags = []string{"dd:2"}
dur, _ := time.ParseDuration("123us")
client.Incr("ic", nil, 1)
client.Decr("dc", nil, 1)
client.Count("cc", 1, nil, 1)
client.Gauge("gg", 10, nil, 1)
client.Histogram("hh", 1, nil, 1)
client.Timing("tt", dur, nil, 1)
client.Set("ss", "ss", nil, 1)
if len(client.commands) != 7 {
t.Errorf("Expected client to have buffered 7 commands, but found %d\n", len(client.commands))
}
client.Set("ss", "xx", nil, 1)
err = client.flush()
if err != nil {
t.Errorf("Error sending: %s", err)
}
if len(client.commands) != 0 {
t.Errorf("Expecting send to flush commands, but found %d\n", len(client.commands))
}
buffer := make([]byte, 4096)
n, err := io.ReadAtLeast(server, buffer, 1)
result := string(buffer[:n])
if err != nil {
t.Error(err)
}
expected := []string{
`foo.ic:1|c|#dd:2`,
`foo.dc:-1|c|#dd:2`,
`foo.cc:1|c|#dd:2`,
`foo.gg:10.000000|g|#dd:2`,
`foo.hh:1.000000|h|#dd:2`,
`foo.tt:0.123000|ms|#dd:2`,
`foo.ss:ss|s|#dd:2`,
`foo.ss:xx|s|#dd:2`,
}
for i, res := range strings.Split(result, "\n") {
if res != expected[i] {
t.Errorf("Got `%s`, expected `%s`", res, expected[i])
}
}
client.Event(&Event{Title: "title1", Text: "text1", Priority: Normal, AlertType: Success, Tags: []string{"tagg"}})
client.SimpleEvent("event1", "text1")
if len(client.commands) != 2 {
t.Errorf("Expected to find %d commands, but found %d\n", 2, len(client.commands))
}
err = client.flush()
if err != nil {
t.Errorf("Error sending: %s", err)
}
if len(client.commands) != 0 {
t.Errorf("Expecting send to flush commands, but found %d\n", len(client.commands))
}
buffer = make([]byte, 1024)
n, err = io.ReadAtLeast(server, buffer, 1)
result = string(buffer[:n])
if err != nil {
t.Error(err)
}
if n == 0 {
t.Errorf("Read 0 bytes but expected more.")
}
expected = []string{
`_e{6,5}:title1|text1|p:normal|t:success|#dd:2,tagg`,
`_e{6,5}:event1|text1|#dd:2`,
}
for i, res := range strings.Split(result, "\n") {
if res != expected[i] {
t.Errorf("Got `%s`, expected `%s`", res, expected[i])
}
}
}
func TestJoinMaxSize(t *testing.T) {
c := Client{}
elements := []string{"abc", "abcd", "ab", "xyz", "foobaz", "x", "wwxxyyzz"}
res, n := c.joinMaxSize(elements, " ", 8)
if len(res) != len(n) && len(res) != 4 {
t.Errorf("Was expecting 4 frames to flush but got: %v - %v", n, res)
}
if n[0] != 2 {
t.Errorf("Was expecting 2 elements in first frame but got: %v", n[0])
}
if string(res[0]) != "abc abcd" {
t.Errorf("Join should have returned \"abc abcd\" in frame, but found: %s", res[0])
}
if n[1] != 2 {
t.Errorf("Was expecting 2 elements in second frame but got: %v - %v", n[1], n)
}
if string(res[1]) != "ab xyz" {
t.Errorf("Join should have returned \"ab xyz\" in frame, but found: %s", res[1])
}
if n[2] != 2 {
t.Errorf("Was expecting 2 elements in third frame but got: %v - %v", n[2], n)
}
if string(res[2]) != "foobaz x" {
t.Errorf("Join should have returned \"foobaz x\" in frame, but found: %s", res[2])
}
if n[3] != 1 {
t.Errorf("Was expecting 1 element in fourth frame but got: %v - %v", n[3], n)
}
if string(res[3]) != "wwxxyyzz" {
t.Errorf("Join should have returned \"wwxxyyzz\" in frame, but found: %s", res[3])
}
res, n = c.joinMaxSize(elements, " ", 11)
if len(res) != len(n) && len(res) != 3 {
t.Errorf("Was expecting 3 frames to flush but got: %v - %v", n, res)
}
if n[0] != 3 {
t.Errorf("Was expecting 3 elements in first frame but got: %v", n[0])
}
if string(res[0]) != "abc abcd ab" {
t.Errorf("Join should have returned \"abc abcd ab\" in frame, but got: %s", res[0])
}
if n[1] != 2 {
t.Errorf("Was expecting 2 elements in second frame but got: %v", n[1])
}
if string(res[1]) != "xyz foobaz" {
t.Errorf("Join should have returned \"xyz foobaz\" in frame, but got: %s", res[1])
}
if n[2] != 2 {
t.Errorf("Was expecting 2 elements in third frame but got: %v", n[2])
}
if string(res[2]) != "x wwxxyyzz" {
t.Errorf("Join should have returned \"x wwxxyyzz\" in frame, but got: %s", res[2])
}
res, n = c.joinMaxSize(elements, " ", 8)
if len(res) != len(n) && len(res) != 7 {
t.Errorf("Was expecting 7 frames to flush but got: %v - %v", n, res)
}
if n[0] != 1 {
t.Errorf("Separator is long, expected a single element in frame but got: %d - %v", n[0], res)
}
if string(res[0]) != "abc" {
t.Errorf("Join should have returned \"abc\" in first frame, but got: %s", res)
}
if n[1] != 1 {
t.Errorf("Separator is long, expected a single element in frame but got: %d - %v", n[1], res)
}
if string(res[1]) != "abcd" {
t.Errorf("Join should have returned \"abcd\" in second frame, but got: %s", res[1])
}
if n[2] != 1 {
t.Errorf("Separator is long, expected a single element in third frame but got: %d - %v", n[2], res)
}
if string(res[2]) != "ab" {
t.Errorf("Join should have returned \"ab\" in third frame, but got: %s", res[2])
}
if n[3] != 1 {
t.Errorf("Separator is long, expected a single element in fourth frame but got: %d - %v", n[3], res)
}
if string(res[3]) != "xyz" {
t.Errorf("Join should have returned \"xyz\" in fourth frame, but got: %s", res[3])
}
if n[4] != 1 {
t.Errorf("Separator is long, expected a single element in fifth frame but got: %d - %v", n[4], res)
}
if string(res[4]) != "foobaz" {
t.Errorf("Join should have returned \"foobaz\" in fifth frame, but got: %s", res[4])
}
if n[5] != 1 {
t.Errorf("Separator is long, expected a single element in sixth frame but got: %d - %v", n[5], res)
}
if string(res[5]) != "x" {
t.Errorf("Join should have returned \"x\" in sixth frame, but got: %s", res[5])
}
if n[6] != 1 {
t.Errorf("Separator is long, expected a single element in seventh frame but got: %d - %v", n[6], res)
}
if string(res[6]) != "wwxxyyzz" {
t.Errorf("Join should have returned \"wwxxyyzz\" in seventh frame, but got: %s", res[6])
}
res, n = c.joinMaxSize(elements[4:], " ", 6)
if len(res) != len(n) && len(res) != 3 {
t.Errorf("Was expecting 3 frames to flush but got: %v - %v", n, res)
}
if n[0] != 1 {
t.Errorf("Element should just fit in frame - expected single element in frame: %d - %v", n[0], res)
}
if string(res[0]) != "foobaz" {
t.Errorf("Join should have returned \"foobaz\" in first frame, but got: %s", res[0])
}
if n[1] != 1 {
t.Errorf("Single element expected in frame, but got. %d - %v", n[1], res)
}
if string(res[1]) != "x" {
t.Errorf("Join should' have returned \"x\" in second frame, but got: %s", res[1])
}
if n[2] != 1 {
t.Errorf("Even though element is greater then max size we still try to send it. %d - %v", n[2], res)
}
if string(res[2]) != "wwxxyyzz" {
t.Errorf("Join should have returned \"wwxxyyzz\" in third frame, but got: %s", res[2])
}
}
func TestSendMsg(t *testing.T) {
addr := "localhost:1201"
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
t.Fatal(err)
}
server, err := net.ListenUDP("udp", udpAddr)
if err != nil {
t.Fatal(err)
}
defer server.Close()
conn, err := net.DialUDP("udp", nil, udpAddr)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
client := &Client{
conn: conn,
bufferLength: 0,
}
err = client.sendMsg(strings.Repeat("x", MaxUDPPayloadSize+1))
if err == nil {
t.Error("Expected error to be returned if message size is bigger than MaxUDPPayloadSize")
}
longMsg := strings.Repeat("x", MaxUDPPayloadSize)
err = client.sendMsg(longMsg)
if err != nil {
t.Errorf("Expected no error to be returned if message size is smaller or equal to MaxUDPPayloadSize, got: %s", err.Error())
}
buffer := make([]byte, MaxUDPPayloadSize+1)
n, err := io.ReadAtLeast(server, buffer, 1)
if err != nil {
t.Fatalf("Expected no error to be returned reading the buffer, got: %s", err.Error())
}
if n != MaxUDPPayloadSize {
t.Fatalf("Failed to read full message from buffer. Got size `%d` expected `%d`", n, MaxUDPPayloadSize)
}
if string(buffer[:n]) != longMsg {
t.Fatalf("The received message did not match what we expect.")
}
client = &Client{
conn: conn,
commands: make([]string, 0, 1),
bufferLength: 1,
}
err = client.sendMsg(strings.Repeat("x", MaxUDPPayloadSize+1))
if err == nil {
t.Error("Expected error to be returned if message size is bigger than MaxUDPPayloadSize")
}
err = client.sendMsg(longMsg)
if err != nil {
t.Errorf("Expected no error to be returned if message size is smaller or equal to MaxUDPPayloadSize, got: %s", err.Error())
}
client.Lock()
err = client.flush()
client.Unlock()
if err != nil {
t.Fatalf("Expected no error to be returned flushing the client, got: %s", err.Error())
}
buffer = make([]byte, MaxUDPPayloadSize+1)
n, err = io.ReadAtLeast(server, buffer, 1)
if err != nil {
t.Fatalf("Expected no error to be returned reading the buffer, got: %s", err.Error())
}
if n != MaxUDPPayloadSize {
t.Fatalf("Failed to read full message from buffer. Got size `%d` expected `%d`", n, MaxUDPPayloadSize)
}
if string(buffer[:n]) != longMsg {
t.Fatalf("The received message did not match what we expect.")
}
}
func TestNilSafe(t *testing.T) {
var c *Client
assertNotPanics(t, func() { c.Close() })
assertNotPanics(t, func() { c.Count("", 0, nil, 1) })
assertNotPanics(t, func() { c.Histogram("", 0, nil, 1) })
assertNotPanics(t, func() { c.Gauge("", 0, nil, 1) })
assertNotPanics(t, func() { c.Set("", "", nil, 1) })
assertNotPanics(t, func() { c.send("", "", nil, 1) })
assertNotPanics(t, func() { c.SimpleEvent("", "") })
}
func TestEvents(t *testing.T) {
matrix := []struct {
event *Event
encoded string
}{
{
NewEvent("Hello", "Something happened to my event"),
`_e{5,30}:Hello|Something happened to my event`,
}, {
&Event{Title: "hi", Text: "okay", AggregationKey: "foo"},
`_e{2,4}:hi|okay|k:foo`,
}, {
&Event{Title: "hi", Text: "okay", AggregationKey: "foo", AlertType: Info},
`_e{2,4}:hi|okay|k:foo|t:info`,
}, {
&Event{Title: "hi", Text: "w/e", AlertType: Error, Priority: Normal},
`_e{2,3}:hi|w/e|p:normal|t:error`,
}, {
&Event{Title: "hi", Text: "uh", Tags: []string{"host:foo", "app:bar"}},
`_e{2,2}:hi|uh|#host:foo,app:bar`,
}, {
&Event{Title: "hi", Text: "line1\nline2", Tags: []string{"hello\nworld"}},
`_e{2,12}:hi|line1\nline2|#helloworld`,
},
}
for _, m := range matrix {
r, err := m.event.Encode()
if err != nil {
t.Errorf("Error encoding: %s\n", err)
continue
}
if r != m.encoded {
t.Errorf("Expected `%s`, got `%s`\n", m.encoded, r)
}
}
e := NewEvent("", "hi")
if _, err := e.Encode(); err == nil {
t.Errorf("Expected error on empty Title.")
}
e = NewEvent("hi", "")
if _, err := e.Encode(); err == nil {
t.Errorf("Expected error on empty Text.")
}
e = NewEvent("hello", "world")
s, err := e.Encode("tag1", "tag2")
if err != nil {
t.Error(err)
}
expected := "_e{5,5}:hello|world|#tag1,tag2"
if s != expected {
t.Errorf("Expected %s, got %s", expected, s)
}
if len(e.Tags) != 0 {
t.Errorf("Modified event in place illegally.")
}
}
func TestServiceChecks(t *testing.T) {
matrix := []struct {
serviceCheck *ServiceCheck
encoded string
}{
{
NewServiceCheck("DataCatService", Ok),
`_sc|DataCatService|0`,
}, {
NewServiceCheck("DataCatService", Warn),
`_sc|DataCatService|1`,
}, {
NewServiceCheck("DataCatService", Critical),
`_sc|DataCatService|2`,
}, {
NewServiceCheck("DataCatService", Unknown),
`_sc|DataCatService|3`,
}, {
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat"},
`_sc|DataCatService|0|h:DataStation.Cat`,
}, {
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here goes valuable message"},
`_sc|DataCatService|0|h:DataStation.Cat|m:Here goes valuable message`,
}, {
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here are some cyrillic chars: к л м н о п р с т у ф х ц ч ш"},
`_sc|DataCatService|0|h:DataStation.Cat|m:Here are some cyrillic chars: к л м н о п р с т у ф х ц ч ш`,
}, {
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here goes valuable message", Tags: []string{"host:foo", "app:bar"}},
`_sc|DataCatService|0|h:DataStation.Cat|#host:foo,app:bar|m:Here goes valuable message`,
}, {
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here goes \n that should be escaped", Tags: []string{"host:foo", "app:b\nar"}},
`_sc|DataCatService|0|h:DataStation.Cat|#host:foo,app:bar|m:Here goes \n that should be escaped`,
}, {
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here goes m: that should be escaped", Tags: []string{"host:foo", "app:bar"}},
`_sc|DataCatService|0|h:DataStation.Cat|#host:foo,app:bar|m:Here goes m\: that should be escaped`,
},
}
for _, m := range matrix {
r, err := m.serviceCheck.Encode()
if err != nil {
t.Errorf("Error encoding: %s\n", err)
continue
}
if r != m.encoded {
t.Errorf("Expected `%s`, got `%s`\n", m.encoded, r)
}
}
sc := NewServiceCheck("", Ok)
if _, err := sc.Encode(); err == nil {
t.Errorf("Expected error on empty Name.")
}
sc = NewServiceCheck("sc", ServiceCheckStatus(5))
if _, err := sc.Encode(); err == nil {
t.Errorf("Expected error on invalid status value.")
}
sc = NewServiceCheck("hello", Warn)
s, err := sc.Encode("tag1", "tag2")
if err != nil {
t.Error(err)
}
expected := "_sc|hello|1|#tag1,tag2"
if s != expected {
t.Errorf("Expected %s, got %s", expected, s)
}
if len(sc.Tags) != 0 {
t.Errorf("Modified serviceCheck in place illegally.")
}
}
// These benchmarks show that using a buffer instead of sprintf-ing together
// a bunch of intermediate strings is 4-5x faster
func BenchmarkFormatNew(b *testing.B) {
b.StopTimer()
c := &Client{}
c.Namespace = "foo.bar."
c.Tags = []string{"app:foo", "host:bar"}
b.StartTimer()
for i := 0; i < b.N; i++ {
c.format("system.cpu.idle", "10", []string{"foo"}, 1)
c.format("system.cpu.load", "0.1", nil, 0.9)
}
}
// Old formatting function, added to client for tests
func (c *Client) formatOld(name, value string, tags []string, rate float64) string {
if rate < 1 {
value = fmt.Sprintf("%s|@%f", value, rate)
}
if c.Namespace != "" {
name = fmt.Sprintf("%s%s", c.Namespace, name)
}
tags = append(c.Tags, tags...)
if len(tags) > 0 {
value = fmt.Sprintf("%s|#%s", value, strings.Join(tags, ","))
}
return fmt.Sprintf("%s:%s", name, value)
}
func BenchmarkFormatOld(b *testing.B) {
b.StopTimer()
c := &Client{}
c.Namespace = "foo.bar."
c.Tags = []string{"app:foo", "host:bar"}
b.StartTimer()
for i := 0; i < b.N; i++ {
c.formatOld("system.cpu.idle", "10", []string{"foo"}, 1)
c.formatOld("system.cpu.load", "0.1", nil, 0.9)
}
}

2
vendor/github.com/RangelReale/osin/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
*.exe
/src

7
vendor/github.com/RangelReale/osin/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,7 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- tip

19
vendor/github.com/RangelReale/osin/CHANGELOG generated vendored Normal file
View File

@@ -0,0 +1,19 @@
2014-06-25
==========
* BREAKING CHANGES:
- Storage interface has 2 new methods, Clone and Close, to better support storages
that need to clone / close in each connection (mgo)
- Client was changed to be an interface instead of an struct. Because of that,
the Storage interface also had to change, as interface is already a pointer.
- HOW TO FIX YOUR CODE:
+ In your Storage, add a Clone function returning itself, and a do nothing Close.
+ In your Storage, replace all *osin.Client with osin.Client (remove the pointer reference)
+ If you used the osin.Client struct directly in your code, change it to osin.DefaultClient,
which is a struct with the same fields that implements the interface.
+ Change all accesses using osin.Client to use the methods instead of the fields directly.
+ You MUST defer Response.Close in all your http handlers, otherwise some
Storages may not clean correctly.
resp := server.NewResponse()
defer resp.Close()

9
vendor/github.com/RangelReale/osin/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,9 @@
Copyright (c) 2013, Rangel Reale
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the SIB IT nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

112
vendor/github.com/RangelReale/osin/README.md generated vendored Normal file
View File

@@ -0,0 +1,112 @@
OSIN
====
[![GoDoc](https://godoc.org/github.com/RangelReale/osin?status.svg)](https://godoc.org/github.com/RangelReale/osin)
Golang OAuth2 server library
----------------------------
OSIN is an OAuth2 server library for the Go language, as specified at
http://tools.ietf.org/html/rfc6749 and http://tools.ietf.org/html/draft-ietf-oauth-v2-10.
It also includes support for PKCE, as specified at https://tools.ietf.org/html/rfc7636,
which increases security for code-exchange flows for public OAuth clients.
Using it, you can build your own OAuth2 authentication service.
The library implements the majority of the specification, like authorization and token endpoints, and authorization code, implicit, resource owner and client credentials grant types.
### Example Server
````go
import (
"github.com/RangelReale/osin"
ex "github.com/RangelReale/osin/example"
)
// ex.NewTestStorage implements the "osin.Storage" interface
server := osin.NewServer(osin.NewServerConfig(), ex.NewTestStorage())
// Authorization code endpoint
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
// HANDLE LOGIN PAGE HERE
ar.Authorized = true
server.FinishAuthorizeRequest(resp, r, ar)
}
osin.OutputJSON(resp, w, r)
})
// Access token endpoint
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAccessRequest(resp, r); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, r, ar)
}
osin.OutputJSON(resp, w, r)
})
http.ListenAndServe(":14000", nil)
````
### Example Access
Open in your web browser:
````
http://localhost:14000/authorize?response_type=code&client_id=1234&redirect_uri=http%3A%2F%2Flocalhost%3A14000%2Fappauth%2Fcode
````
### Storage backends
There is a mock available at [example/teststorage.go](/example/teststorage.go) which you can use as a guide for writing your own.
You might want to check out other implementations for common database management systems as well:
* [PostgreSQL](https://github.com/ory-am/osin-storage)
* [MongoDB](https://github.com/martint17r/osin-mongo-storage)
* [RethinkDB](https://github.com/ahmet/osin-rethinkdb)
* [DynamoDB](https://github.com/uniplaces/osin-dynamodb)
* [Couchbase](https://github.com/elgris/osin-couchbase-storage)
* [MySQL](https://github.com/felipeweb/osin-mysql)
* [Redis](https://github.com/ShaleApps/osinredis)
### License
The code is licensed using "New BSD" license.
### Author
Rangel Reale
rangelreale@gmail.com
### Changes
2014-06-25
==========
* BREAKING CHANGES:
- Storage interface has 2 new methods, Clone and Close, to better support storages
that need to clone / close in each connection (mgo)
- Client was changed to be an interface instead of an struct. Because of that,
the Storage interface also had to change, as interface is already a pointer.
- HOW TO FIX YOUR CODE:
+ In your Storage, add a Clone function returning itself, and a do nothing Close.
+ In your Storage, replace all *osin.Client with osin.Client (remove the pointer reference)
+ If you used the osin.Client struct directly in your code, change it to osin.DefaultClient,
which is a struct with the same fields that implements the interface.
+ Change all accesses using osin.Client to use the methods instead of the fields directly.
+ You MUST defer Response.Close in all your http handlers, otherwise some
Storages may not clean correctly.
resp := server.NewResponse()
defer resp.Close()

562
vendor/github.com/RangelReale/osin/access.go generated vendored Normal file
View File

@@ -0,0 +1,562 @@
package osin
import (
"crypto/sha256"
"encoding/base64"
"errors"
"net/http"
"strings"
"time"
)
// AccessRequestType is the type for OAuth param `grant_type`
type AccessRequestType string
const (
AUTHORIZATION_CODE AccessRequestType = "authorization_code"
REFRESH_TOKEN AccessRequestType = "refresh_token"
PASSWORD AccessRequestType = "password"
CLIENT_CREDENTIALS AccessRequestType = "client_credentials"
ASSERTION AccessRequestType = "assertion"
IMPLICIT AccessRequestType = "__implicit"
)
// AccessRequest is a request for access tokens
type AccessRequest struct {
Type AccessRequestType
Code string
Client Client
AuthorizeData *AuthorizeData
AccessData *AccessData
// Force finish to use this access data, to allow access data reuse
ForceAccessData *AccessData
RedirectUri string
Scope string
Username string
Password string
AssertionType string
Assertion string
// Set if request is authorized
Authorized bool
// Token expiration in seconds. Change if different from default
Expiration int32
// Set if a refresh token should be generated
GenerateRefresh bool
// Data to be passed to storage. Not used by the library.
UserData interface{}
// HttpRequest *http.Request for special use
HttpRequest *http.Request
// Optional code_verifier as described in rfc7636
CodeVerifier string
}
// AccessData represents an access grant (tokens, expiration, client, etc)
type AccessData struct {
// Client information
Client Client
// Authorize data, for authorization code
AuthorizeData *AuthorizeData
// Previous access data, for refresh token
AccessData *AccessData
// Access token
AccessToken string
// Refresh Token. Can be blank
RefreshToken string
// Token expiration in seconds
ExpiresIn int32
// Requested scope
Scope string
// Redirect Uri from request
RedirectUri string
// Date created
CreatedAt time.Time
// Data to be passed to storage. Not used by the library.
UserData interface{}
}
// IsExpired returns true if access expired
func (d *AccessData) IsExpired() bool {
return d.IsExpiredAt(time.Now())
}
// IsExpiredAt returns true if access expires at time 't'
func (d *AccessData) IsExpiredAt(t time.Time) bool {
return d.ExpireAt().Before(t)
}
// ExpireAt returns the expiration date
func (d *AccessData) ExpireAt() time.Time {
return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
}
// AccessTokenGen generates access tokens
type AccessTokenGen interface {
GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error)
}
// HandleAccessRequest is the http.HandlerFunc for handling access token requests
func (s *Server) HandleAccessRequest(w *Response, r *http.Request) *AccessRequest {
// Only allow GET or POST
if r.Method == "GET" {
if !s.Config.AllowGetAccessRequest {
w.SetError(E_INVALID_REQUEST, "")
w.InternalError = errors.New("Request must be POST")
return nil
}
} else if r.Method != "POST" {
w.SetError(E_INVALID_REQUEST, "")
w.InternalError = errors.New("Request must be POST")
return nil
}
err := r.ParseForm()
if err != nil {
w.SetError(E_INVALID_REQUEST, "")
w.InternalError = err
return nil
}
grantType := AccessRequestType(r.Form.Get("grant_type"))
if s.Config.AllowedAccessTypes.Exists(grantType) {
switch grantType {
case AUTHORIZATION_CODE:
return s.handleAuthorizationCodeRequest(w, r)
case REFRESH_TOKEN:
return s.handleRefreshTokenRequest(w, r)
case PASSWORD:
return s.handlePasswordRequest(w, r)
case CLIENT_CREDENTIALS:
return s.handleClientCredentialsRequest(w, r)
case ASSERTION:
return s.handleAssertionRequest(w, r)
}
}
w.SetError(E_UNSUPPORTED_GRANT_TYPE, "")
return nil
}
func (s *Server) handleAuthorizationCodeRequest(w *Response, r *http.Request) *AccessRequest {
// get client authentication
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
if auth == nil {
return nil
}
// generate access token
ret := &AccessRequest{
Type: AUTHORIZATION_CODE,
Code: r.Form.Get("code"),
CodeVerifier: r.Form.Get("code_verifier"),
RedirectUri: r.Form.Get("redirect_uri"),
GenerateRefresh: true,
Expiration: s.Config.AccessExpiration,
HttpRequest: r,
}
// "code" is required
if ret.Code == "" {
w.SetError(E_INVALID_GRANT, "")
return nil
}
// must have a valid client
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
return nil
}
// must be a valid authorization code
var err error
ret.AuthorizeData, err = w.Storage.LoadAuthorize(ret.Code)
if err != nil {
w.SetError(E_INVALID_GRANT, "")
w.InternalError = err
return nil
}
if ret.AuthorizeData == nil {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if ret.AuthorizeData.Client == nil {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if ret.AuthorizeData.Client.GetRedirectUri() == "" {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if ret.AuthorizeData.IsExpiredAt(s.Now()) {
w.SetError(E_INVALID_GRANT, "")
return nil
}
// code must be from the client
if ret.AuthorizeData.Client.GetId() != ret.Client.GetId() {
w.SetError(E_INVALID_GRANT, "")
return nil
}
// check redirect uri
if ret.RedirectUri == "" {
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
}
if err = ValidateUriList(ret.Client.GetRedirectUri(), ret.RedirectUri, s.Config.RedirectUriSeparator); err != nil {
w.SetError(E_INVALID_REQUEST, "")
w.InternalError = err
return nil
}
if ret.AuthorizeData.RedirectUri != ret.RedirectUri {
w.SetError(E_INVALID_REQUEST, "")
w.InternalError = errors.New("Redirect uri is different")
return nil
}
// Verify PKCE, if present in the authorization data
if len(ret.AuthorizeData.CodeChallenge) > 0 {
// https://tools.ietf.org/html/rfc7636#section-4.1
if matched := pkceMatcher.MatchString(ret.CodeVerifier); !matched {
w.SetError(E_INVALID_REQUEST, "code_verifier invalid (rfc7636)")
w.InternalError = errors.New("code_verifier has invalid format")
return nil
}
// https: //tools.ietf.org/html/rfc7636#section-4.6
codeVerifier := ""
switch ret.AuthorizeData.CodeChallengeMethod {
case "", PKCE_PLAIN:
codeVerifier = ret.CodeVerifier
case PKCE_S256:
hash := sha256.Sum256([]byte(ret.CodeVerifier))
codeVerifier = base64.RawURLEncoding.EncodeToString(hash[:])
default:
w.SetError(E_INVALID_REQUEST, "code_challenge_method transform algorithm not supported (rfc7636)")
return nil
}
if codeVerifier != ret.AuthorizeData.CodeChallenge {
w.SetError(E_INVALID_GRANT, "code_verifier invalid (rfc7636)")
w.InternalError = errors.New("code_verifier failed comparison with code_challenge")
return nil
}
}
// set rest of data
ret.Scope = ret.AuthorizeData.Scope
ret.UserData = ret.AuthorizeData.UserData
return ret
}
func extraScopes(access_scopes, refresh_scopes string) bool {
access_scopes_list := strings.Split(access_scopes, " ")
refresh_scopes_list := strings.Split(refresh_scopes, " ")
access_map := make(map[string]int)
for _, scope := range access_scopes_list {
if scope == "" {
continue
}
access_map[scope] = 1
}
for _, scope := range refresh_scopes_list {
if scope == "" {
continue
}
if _, ok := access_map[scope]; !ok {
return true
}
}
return false
}
func (s *Server) handleRefreshTokenRequest(w *Response, r *http.Request) *AccessRequest {
// get client authentication
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
if auth == nil {
return nil
}
// generate access token
ret := &AccessRequest{
Type: REFRESH_TOKEN,
Code: r.Form.Get("refresh_token"),
Scope: r.Form.Get("scope"),
GenerateRefresh: true,
Expiration: s.Config.AccessExpiration,
HttpRequest: r,
}
// "refresh_token" is required
if ret.Code == "" {
w.SetError(E_INVALID_GRANT, "")
return nil
}
// must have a valid client
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
return nil
}
// must be a valid refresh code
var err error
ret.AccessData, err = w.Storage.LoadRefresh(ret.Code)
if err != nil {
w.SetError(E_INVALID_GRANT, "")
w.InternalError = err
return nil
}
if ret.AccessData == nil {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if ret.AccessData.Client == nil {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if ret.AccessData.Client.GetRedirectUri() == "" {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
// client must be the same as the previous token
if ret.AccessData.Client.GetId() != ret.Client.GetId() {
w.SetError(E_INVALID_CLIENT, "")
w.InternalError = errors.New("Client id must be the same from previous token")
return nil
}
// set rest of data
ret.RedirectUri = ret.AccessData.RedirectUri
ret.UserData = ret.AccessData.UserData
if ret.Scope == "" {
ret.Scope = ret.AccessData.Scope
}
if extraScopes(ret.AccessData.Scope, ret.Scope) {
w.SetError(E_ACCESS_DENIED, "")
w.InternalError = errors.New("the requested scope must not include any scope not originally granted by the resource owner")
return nil
}
return ret
}
func (s *Server) handlePasswordRequest(w *Response, r *http.Request) *AccessRequest {
// get client authentication
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
if auth == nil {
return nil
}
// generate access token
ret := &AccessRequest{
Type: PASSWORD,
Username: r.Form.Get("username"),
Password: r.Form.Get("password"),
Scope: r.Form.Get("scope"),
GenerateRefresh: true,
Expiration: s.Config.AccessExpiration,
HttpRequest: r,
}
// "username" and "password" is required
if ret.Username == "" || ret.Password == "" {
w.SetError(E_INVALID_GRANT, "")
return nil
}
// must have a valid client
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
return nil
}
// set redirect uri
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
return ret
}
func (s *Server) handleClientCredentialsRequest(w *Response, r *http.Request) *AccessRequest {
// get client authentication
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
if auth == nil {
return nil
}
// generate access token
ret := &AccessRequest{
Type: CLIENT_CREDENTIALS,
Scope: r.Form.Get("scope"),
GenerateRefresh: false,
Expiration: s.Config.AccessExpiration,
HttpRequest: r,
}
// must have a valid client
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
return nil
}
// set redirect uri
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
return ret
}
func (s *Server) handleAssertionRequest(w *Response, r *http.Request) *AccessRequest {
// get client authentication
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
if auth == nil {
return nil
}
// generate access token
ret := &AccessRequest{
Type: ASSERTION,
Scope: r.Form.Get("scope"),
AssertionType: r.Form.Get("assertion_type"),
Assertion: r.Form.Get("assertion"),
GenerateRefresh: false, // assertion should NOT generate a refresh token, per the RFC
Expiration: s.Config.AccessExpiration,
HttpRequest: r,
}
// "assertion_type" and "assertion" is required
if ret.AssertionType == "" || ret.Assertion == "" {
w.SetError(E_INVALID_GRANT, "")
return nil
}
// must have a valid client
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
return nil
}
// set redirect uri
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
return ret
}
func (s *Server) FinishAccessRequest(w *Response, r *http.Request, ar *AccessRequest) {
// don't process if is already an error
if w.IsError {
return
}
redirectUri := r.Form.Get("redirect_uri")
// Get redirect uri from AccessRequest if it's there (e.g., refresh token request)
if ar.RedirectUri != "" {
redirectUri = ar.RedirectUri
}
if ar.Authorized {
var ret *AccessData
var err error
if ar.ForceAccessData == nil {
// generate access token
ret = &AccessData{
Client: ar.Client,
AuthorizeData: ar.AuthorizeData,
AccessData: ar.AccessData,
RedirectUri: redirectUri,
CreatedAt: s.Now(),
ExpiresIn: ar.Expiration,
UserData: ar.UserData,
Scope: ar.Scope,
}
// generate access token
ret.AccessToken, ret.RefreshToken, err = s.AccessTokenGen.GenerateAccessToken(ret, ar.GenerateRefresh)
if err != nil {
w.SetError(E_SERVER_ERROR, "")
w.InternalError = err
return
}
} else {
ret = ar.ForceAccessData
}
// save access token
if err = w.Storage.SaveAccess(ret); err != nil {
w.SetError(E_SERVER_ERROR, "")
w.InternalError = err
return
}
// remove authorization token
if ret.AuthorizeData != nil {
w.Storage.RemoveAuthorize(ret.AuthorizeData.Code)
}
// remove previous access token
if ret.AccessData != nil && !s.Config.RetainTokenAfterRefresh {
if ret.AccessData.RefreshToken != "" {
w.Storage.RemoveRefresh(ret.AccessData.RefreshToken)
}
w.Storage.RemoveAccess(ret.AccessData.AccessToken)
}
// output data
w.Output["access_token"] = ret.AccessToken
w.Output["token_type"] = s.Config.TokenType
w.Output["expires_in"] = ret.ExpiresIn
if ret.RefreshToken != "" {
w.Output["refresh_token"] = ret.RefreshToken
}
if ret.Scope != "" {
w.Output["scope"] = ret.Scope
}
} else {
w.SetError(E_ACCESS_DENIED, "")
}
}
// Helper Functions
// getClient looks up and authenticates the basic auth using the given
// storage. Sets an error on the response if auth fails or a server error occurs.
func getClient(auth *BasicAuth, storage Storage, w *Response) Client {
client, err := storage.GetClient(auth.Username)
if err == ErrNotFound {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if err != nil {
w.SetError(E_SERVER_ERROR, "")
w.InternalError = err
return nil
}
if client == nil {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if !CheckClientSecret(client, auth.Password) {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if client.GetRedirectUri() == "" {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
return client
}

489
vendor/github.com/RangelReale/osin/access_test.go generated vendored Normal file
View File

@@ -0,0 +1,489 @@
package osin
import (
"net/http"
"net/url"
"testing"
"time"
)
func TestAccessAuthorizationCode(t *testing.T) {
sconfig := NewServerConfig()
sconfig.AllowedAccessTypes = AllowedAccessType{AUTHORIZATION_CODE}
server := NewServer(sconfig, NewTestingStorage())
server.AccessTokenGen = &TestingAccessTokenGen{}
resp := server.NewResponse()
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.SetBasicAuth("1234", "aabbccdd")
req.Form = make(url.Values)
req.Form.Set("grant_type", string(AUTHORIZATION_CODE))
req.Form.Set("code", "9999")
req.Form.Set("state", "a")
req.PostForm = make(url.Values)
if ar := server.HandleAccessRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != DATA {
t.Fatalf("Response should be data")
}
if d := resp.Output["access_token"]; d != "1" {
t.Fatalf("Unexpected access token: %s", d)
}
if d := resp.Output["refresh_token"]; d != "r1" {
t.Fatalf("Unexpected refresh token: %s", d)
}
}
func TestAccessRefreshToken(t *testing.T) {
sconfig := NewServerConfig()
sconfig.AllowedAccessTypes = AllowedAccessType{REFRESH_TOKEN}
server := NewServer(sconfig, NewTestingStorage())
server.AccessTokenGen = &TestingAccessTokenGen{}
resp := server.NewResponse()
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.SetBasicAuth("1234", "aabbccdd")
req.Form = make(url.Values)
req.Form.Set("grant_type", string(REFRESH_TOKEN))
req.Form.Set("refresh_token", "r9999")
req.Form.Set("state", "a")
req.PostForm = make(url.Values)
if ar := server.HandleAccessRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if _, err := server.Storage.LoadRefresh("r9999"); err == nil {
t.Fatalf("token was not deleted")
}
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != DATA {
t.Fatalf("Response should be data")
}
if d := resp.Output["access_token"]; d != "1" {
t.Fatalf("Unexpected access token: %s", d)
}
if d := resp.Output["refresh_token"]; d != "r1" {
t.Fatalf("Unexpected refresh token: %s", d)
}
}
func TestAccessRefreshTokenSaveToken(t *testing.T) {
sconfig := NewServerConfig()
sconfig.AllowedAccessTypes = AllowedAccessType{REFRESH_TOKEN}
server := NewServer(sconfig, NewTestingStorage())
server.AccessTokenGen = &TestingAccessTokenGen{}
server.Config.RetainTokenAfterRefresh = true
resp := server.NewResponse()
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.SetBasicAuth("1234", "aabbccdd")
req.Form = make(url.Values)
req.Form.Set("grant_type", string(REFRESH_TOKEN))
req.Form.Set("refresh_token", "r9999")
req.Form.Set("state", "a")
req.PostForm = make(url.Values)
if ar := server.HandleAccessRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if _, err := server.Storage.LoadRefresh("r9999"); err != nil {
t.Fatalf("token incorrectly deleted: %s", err.Error())
}
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != DATA {
t.Fatalf("Response should be data")
}
if d := resp.Output["access_token"]; d != "1" {
t.Fatalf("Unexpected access token: %s", d)
}
if d := resp.Output["refresh_token"]; d != "r1" {
t.Fatalf("Unexpected refresh token: %s", d)
}
}
func TestAccessPassword(t *testing.T) {
sconfig := NewServerConfig()
sconfig.AllowedAccessTypes = AllowedAccessType{PASSWORD}
server := NewServer(sconfig, NewTestingStorage())
server.AccessTokenGen = &TestingAccessTokenGen{}
resp := server.NewResponse()
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.SetBasicAuth("1234", "aabbccdd")
req.Form = make(url.Values)
req.Form.Set("grant_type", string(PASSWORD))
req.Form.Set("username", "testing")
req.Form.Set("password", "testing")
req.Form.Set("state", "a")
req.PostForm = make(url.Values)
if ar := server.HandleAccessRequest(resp, req); ar != nil {
ar.Authorized = ar.Username == "testing" && ar.Password == "testing"
server.FinishAccessRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != DATA {
t.Fatalf("Response should be data")
}
if d := resp.Output["access_token"]; d != "1" {
t.Fatalf("Unexpected access token: %s", d)
}
if d := resp.Output["refresh_token"]; d != "r1" {
t.Fatalf("Unexpected refresh token: %s", d)
}
}
func TestAccessClientCredentials(t *testing.T) {
sconfig := NewServerConfig()
sconfig.AllowedAccessTypes = AllowedAccessType{CLIENT_CREDENTIALS}
server := NewServer(sconfig, NewTestingStorage())
server.AccessTokenGen = &TestingAccessTokenGen{}
resp := server.NewResponse()
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.SetBasicAuth("1234", "aabbccdd")
req.Form = make(url.Values)
req.Form.Set("grant_type", string(CLIENT_CREDENTIALS))
req.Form.Set("state", "a")
req.PostForm = make(url.Values)
if ar := server.HandleAccessRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != DATA {
t.Fatalf("Response should be data")
}
if d := resp.Output["access_token"]; d != "1" {
t.Fatalf("Unexpected access token: %s", d)
}
if d, dok := resp.Output["refresh_token"]; dok {
t.Fatalf("Refresh token should not be generated: %s", d)
}
}
func TestExtraScopes(t *testing.T) {
if extraScopes("", "") == true {
t.Fatalf("extraScopes returned true with empty scopes")
}
if extraScopes("a", "") == true {
t.Fatalf("extraScopes returned true with less scopes")
}
if extraScopes("a,b", "b,a") == true {
t.Fatalf("extraScopes returned true with matching scopes")
}
if extraScopes("a,b", "b,a,c") == false {
t.Fatalf("extraScopes returned false with extra scopes")
}
if extraScopes("", "a") == false {
t.Fatalf("extraScopes returned false with extra scopes")
}
}
// clientWithoutMatcher just implements the base Client interface
type clientWithoutMatcher struct {
Id string
Secret string
RedirectUri string
}
func (c *clientWithoutMatcher) GetId() string { return c.Id }
func (c *clientWithoutMatcher) GetSecret() string { return c.Secret }
func (c *clientWithoutMatcher) GetRedirectUri() string { return c.RedirectUri }
func (c *clientWithoutMatcher) GetUserData() interface{} { return nil }
func TestGetClientWithoutMatcher(t *testing.T) {
myclient := &clientWithoutMatcher{
Id: "myclient",
Secret: "myclientsecret",
RedirectUri: "http://www.example.com",
}
storage := &TestingStorage{clients: map[string]Client{myclient.Id: myclient}}
// Ensure bad secret fails
{
auth := &BasicAuth{
Username: "myclient",
Password: "invalidsecret",
}
w := &Response{}
client := getClient(auth, storage, w)
if client != nil {
t.Errorf("Expected error, got client: %v", client)
}
if !w.IsError {
t.Error("No error in response")
}
if w.ErrorId != E_UNAUTHORIZED_CLIENT {
t.Errorf("Expected error %v, got %v", E_UNAUTHORIZED_CLIENT, w.ErrorId)
}
}
// Ensure nonexistent client fails
{
auth := &BasicAuth{
Username: "nonexistent",
Password: "nonexistent",
}
w := &Response{}
client := getClient(auth, storage, w)
if client != nil {
t.Errorf("Expected error, got client: %v", client)
}
if !w.IsError {
t.Error("No error in response")
}
if w.ErrorId != E_UNAUTHORIZED_CLIENT {
t.Errorf("Expected error %v, got %v", E_UNAUTHORIZED_CLIENT, w.ErrorId)
}
}
// Ensure good secret works
{
auth := &BasicAuth{
Username: "myclient",
Password: "myclientsecret",
}
w := &Response{}
client := getClient(auth, storage, w)
if client != myclient {
t.Errorf("Expected client, got nil with response: %v", w)
}
}
}
// clientWithMatcher implements the base Client interface and the ClientSecretMatcher interface
type clientWithMatcher struct {
Id string
Secret string
RedirectUri string
}
func (c *clientWithMatcher) GetId() string { return c.Id }
func (c *clientWithMatcher) GetSecret() string { panic("called GetSecret"); return "" }
func (c *clientWithMatcher) GetRedirectUri() string { return c.RedirectUri }
func (c *clientWithMatcher) GetUserData() interface{} { return nil }
func (c *clientWithMatcher) ClientSecretMatches(secret string) bool {
return secret == c.Secret
}
func TestGetClientSecretMatcher(t *testing.T) {
myclient := &clientWithMatcher{
Id: "myclient",
Secret: "myclientsecret",
RedirectUri: "http://www.example.com",
}
storage := &TestingStorage{clients: map[string]Client{myclient.Id: myclient}}
// Ensure bad secret fails, but does not panic (doesn't call GetSecret)
{
auth := &BasicAuth{
Username: "myclient",
Password: "invalidsecret",
}
w := &Response{}
client := getClient(auth, storage, w)
if client != nil {
t.Errorf("Expected error, got client: %v", client)
}
}
// Ensure good secret works, but does not panic (doesn't call GetSecret)
{
auth := &BasicAuth{
Username: "myclient",
Password: "myclientsecret",
}
w := &Response{}
client := getClient(auth, storage, w)
if client != myclient {
t.Errorf("Expected client, got nil with response: %v", w)
}
}
}
func TestAccessAuthorizationCodePKCE(t *testing.T) {
testcases := map[string]struct {
Challenge string
ChallengeMethod string
Verifier string
ExpectedError string
}{
"good, plain": {
Challenge: "12345678901234567890123456789012345678901234567890",
Verifier: "12345678901234567890123456789012345678901234567890",
},
"bad, plain": {
Challenge: "12345678901234567890123456789012345678901234567890",
Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
ExpectedError: "invalid_grant",
},
"good, S256": {
Challenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
ChallengeMethod: "S256",
Verifier: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
},
"bad, S256": {
Challenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
ChallengeMethod: "S256",
Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
ExpectedError: "invalid_grant",
},
"missing from storage": {
Challenge: "",
ChallengeMethod: "",
Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
},
}
for k, test := range testcases {
testStorage := NewTestingStorage()
sconfig := NewServerConfig()
sconfig.AllowedAccessTypes = AllowedAccessType{AUTHORIZATION_CODE}
server := NewServer(sconfig, testStorage)
server.AccessTokenGen = &TestingAccessTokenGen{}
server.Storage.SaveAuthorize(&AuthorizeData{
Client: testStorage.clients["public-client"],
Code: "pkce-code",
ExpiresIn: 3600,
CreatedAt: time.Now(),
RedirectUri: "http://localhost:14000/appauth",
CodeChallenge: test.Challenge,
CodeChallengeMethod: test.ChallengeMethod,
})
resp := server.NewResponse()
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.SetBasicAuth("public-client", "")
req.Form = make(url.Values)
req.Form.Set("grant_type", string(AUTHORIZATION_CODE))
req.Form.Set("code", "pkce-code")
req.Form.Set("state", "a")
req.Form.Set("code_verifier", test.Verifier)
req.PostForm = make(url.Values)
if ar := server.HandleAccessRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, req, ar)
}
if resp.IsError {
if test.ExpectedError == "" || test.ExpectedError != resp.ErrorId {
t.Errorf("%s: unexpected error: %v, %v", k, resp.ErrorId, resp.StatusText)
continue
}
}
if test.ExpectedError == "" {
if resp.Type != DATA {
t.Fatalf("%s: Response should be data", k)
}
if d := resp.Output["access_token"]; d != "1" {
t.Fatalf("%s: Unexpected access token: %s", k, d)
}
if d := resp.Output["refresh_token"]; d != "r1" {
t.Fatalf("%s: Unexpected refresh token: %s", k, d)
}
}
}
}

275
vendor/github.com/RangelReale/osin/authorize.go generated vendored Normal file
View File

@@ -0,0 +1,275 @@
package osin
import (
"net/http"
"net/url"
"regexp"
"time"
)
// AuthorizeRequestType is the type for OAuth param `response_type`
type AuthorizeRequestType string
const (
CODE AuthorizeRequestType = "code"
TOKEN AuthorizeRequestType = "token"
PKCE_PLAIN = "plain"
PKCE_S256 = "S256"
)
var (
pkceMatcher = regexp.MustCompile("^[a-zA-Z0-9~._-]{43,128}$")
)
// Authorize request information
type AuthorizeRequest struct {
Type AuthorizeRequestType
Client Client
Scope string
RedirectUri string
State string
// Set if request is authorized
Authorized bool
// Token expiration in seconds. Change if different from default.
// If type = TOKEN, this expiration will be for the ACCESS token.
Expiration int32
// Data to be passed to storage. Not used by the library.
UserData interface{}
// HttpRequest *http.Request for special use
HttpRequest *http.Request
// Optional code_challenge as described in rfc7636
CodeChallenge string
// Optional code_challenge_method as described in rfc7636
CodeChallengeMethod string
}
// Authorization data
type AuthorizeData struct {
// Client information
Client Client
// Authorization code
Code string
// Token expiration in seconds
ExpiresIn int32
// Requested scope
Scope string
// Redirect Uri from request
RedirectUri string
// State data from request
State string
// Date created
CreatedAt time.Time
// Data to be passed to storage. Not used by the library.
UserData interface{}
// Optional code_challenge as described in rfc7636
CodeChallenge string
// Optional code_challenge_method as described in rfc7636
CodeChallengeMethod string
}
// IsExpired is true if authorization expired
func (d *AuthorizeData) IsExpired() bool {
return d.IsExpiredAt(time.Now())
}
// IsExpired is true if authorization expires at time 't'
func (d *AuthorizeData) IsExpiredAt(t time.Time) bool {
return d.ExpireAt().Before(t)
}
// ExpireAt returns the expiration date
func (d *AuthorizeData) ExpireAt() time.Time {
return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
}
// AuthorizeTokenGen is the token generator interface
type AuthorizeTokenGen interface {
GenerateAuthorizeToken(data *AuthorizeData) (string, error)
}
// HandleAuthorizeRequest is the main http.HandlerFunc for handling
// authorization requests
func (s *Server) HandleAuthorizeRequest(w *Response, r *http.Request) *AuthorizeRequest {
r.ParseForm()
// create the authorization request
unescapedUri, err := url.QueryUnescape(r.Form.Get("redirect_uri"))
if err != nil {
w.SetErrorState(E_INVALID_REQUEST, "", "")
w.InternalError = err
return nil
}
ret := &AuthorizeRequest{
State: r.Form.Get("state"),
Scope: r.Form.Get("scope"),
RedirectUri: unescapedUri,
Authorized: false,
HttpRequest: r,
}
// must have a valid client
ret.Client, err = w.Storage.GetClient(r.Form.Get("client_id"))
if err == ErrNotFound {
w.SetErrorState(E_UNAUTHORIZED_CLIENT, "", ret.State)
return nil
}
if err != nil {
w.SetErrorState(E_SERVER_ERROR, "", ret.State)
w.InternalError = err
return nil
}
if ret.Client == nil {
w.SetErrorState(E_UNAUTHORIZED_CLIENT, "", ret.State)
return nil
}
if ret.Client.GetRedirectUri() == "" {
w.SetErrorState(E_UNAUTHORIZED_CLIENT, "", ret.State)
return nil
}
// check redirect uri, if there are multiple client redirect uri's
// don't set the uri
if ret.RedirectUri == "" && FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator) == ret.Client.GetRedirectUri() {
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
}
if err = ValidateUriList(ret.Client.GetRedirectUri(), ret.RedirectUri, s.Config.RedirectUriSeparator); err != nil {
w.SetErrorState(E_INVALID_REQUEST, "", ret.State)
w.InternalError = err
return nil
}
w.SetRedirect(ret.RedirectUri)
requestType := AuthorizeRequestType(r.Form.Get("response_type"))
if s.Config.AllowedAuthorizeTypes.Exists(requestType) {
switch requestType {
case CODE:
ret.Type = CODE
ret.Expiration = s.Config.AuthorizationExpiration
// Optional PKCE support (https://tools.ietf.org/html/rfc7636)
if codeChallenge := r.Form.Get("code_challenge"); len(codeChallenge) == 0 {
if s.Config.RequirePKCEForPublicClients && CheckClientSecret(ret.Client, "") {
// https://tools.ietf.org/html/rfc7636#section-4.4.1
w.SetErrorState(E_INVALID_REQUEST, "code_challenge (rfc7636) required for public clients", ret.State)
return nil
}
} else {
codeChallengeMethod := r.Form.Get("code_challenge_method")
// allowed values are "plain" (default) and "S256", per https://tools.ietf.org/html/rfc7636#section-4.3
if len(codeChallengeMethod) == 0 {
codeChallengeMethod = PKCE_PLAIN
}
if codeChallengeMethod != PKCE_PLAIN && codeChallengeMethod != PKCE_S256 {
// https://tools.ietf.org/html/rfc7636#section-4.4.1
w.SetErrorState(E_INVALID_REQUEST, "code_challenge_method transform algorithm not supported (rfc7636)", ret.State)
return nil
}
// https://tools.ietf.org/html/rfc7636#section-4.2
if matched := pkceMatcher.MatchString(codeChallenge); !matched {
w.SetErrorState(E_INVALID_REQUEST, "code_challenge invalid (rfc7636)", ret.State)
return nil
}
ret.CodeChallenge = codeChallenge
ret.CodeChallengeMethod = codeChallengeMethod
}
case TOKEN:
ret.Type = TOKEN
ret.Expiration = s.Config.AccessExpiration
}
return ret
}
w.SetErrorState(E_UNSUPPORTED_RESPONSE_TYPE, "", ret.State)
return nil
}
func (s *Server) FinishAuthorizeRequest(w *Response, r *http.Request, ar *AuthorizeRequest) {
// don't process if is already an error
if w.IsError {
return
}
// force redirect response
w.SetRedirect(ar.RedirectUri)
if ar.Authorized {
if ar.Type == TOKEN {
w.SetRedirectFragment(true)
// generate token directly
ret := &AccessRequest{
Type: IMPLICIT,
Code: "",
Client: ar.Client,
RedirectUri: ar.RedirectUri,
Scope: ar.Scope,
GenerateRefresh: false, // per the RFC, should NOT generate a refresh token in this case
Authorized: true,
Expiration: ar.Expiration,
UserData: ar.UserData,
}
s.FinishAccessRequest(w, r, ret)
if ar.State != "" && w.InternalError == nil {
w.Output["state"] = ar.State
}
} else {
// generate authorization token
ret := &AuthorizeData{
Client: ar.Client,
CreatedAt: s.Now(),
ExpiresIn: ar.Expiration,
RedirectUri: ar.RedirectUri,
State: ar.State,
Scope: ar.Scope,
UserData: ar.UserData,
// Optional PKCE challenge
CodeChallenge: ar.CodeChallenge,
CodeChallengeMethod: ar.CodeChallengeMethod,
}
// generate token code
code, err := s.AuthorizeTokenGen.GenerateAuthorizeToken(ret)
if err != nil {
w.SetErrorState(E_SERVER_ERROR, "", ar.State)
w.InternalError = err
return
}
ret.Code = code
// save authorization token
if err = w.Storage.SaveAuthorize(ret); err != nil {
w.SetErrorState(E_SERVER_ERROR, "", ar.State)
w.InternalError = err
return
}
// redirect with code
w.Output["code"] = ret.Code
w.Output["state"] = ret.State
}
} else {
// redirect with error
w.SetErrorState(E_ACCESS_DENIED, "", ar.State)
}
}

296
vendor/github.com/RangelReale/osin/authorize_test.go generated vendored Normal file
View File

@@ -0,0 +1,296 @@
package osin
import (
"net/http"
"net/url"
"strings"
"testing"
)
func TestAuthorizeCode(t *testing.T) {
sconfig := NewServerConfig()
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE}
server := NewServer(sconfig, NewTestingStorage())
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
resp := server.NewResponse()
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Form = make(url.Values)
req.Form.Set("response_type", string(CODE))
req.Form.Set("client_id", "1234")
req.Form.Set("state", "a")
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAuthorizeRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != REDIRECT {
t.Fatalf("Response should be a redirect")
}
if d := resp.Output["code"]; d != "1" {
t.Fatalf("Unexpected authorization code: %s", d)
}
}
func TestAuthorizeToken(t *testing.T) {
sconfig := NewServerConfig()
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{TOKEN}
server := NewServer(sconfig, NewTestingStorage())
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
server.AccessTokenGen = &TestingAccessTokenGen{}
resp := server.NewResponse()
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Form = make(url.Values)
req.Form.Set("response_type", string(TOKEN))
req.Form.Set("client_id", "1234")
req.Form.Set("state", "a")
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAuthorizeRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != REDIRECT || !resp.RedirectInFragment {
t.Fatalf("Response should be a redirect with fragment")
}
if d := resp.Output["access_token"]; d != "1" {
t.Fatalf("Unexpected access token: %s", d)
}
}
func TestAuthorizeTokenWithInvalidClient(t *testing.T) {
sconfig := NewServerConfig()
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{TOKEN}
server := NewServer(sconfig, NewTestingStorage())
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
server.AccessTokenGen = &TestingAccessTokenGen{}
resp := server.NewResponse()
redirectUri := "http://redirecturi.com"
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Form = make(url.Values)
req.Form.Set("response_type", string(TOKEN))
req.Form.Set("client_id", "invalidclient")
req.Form.Set("state", "a")
req.Form.Set("redirect_uri", redirectUri)
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAuthorizeRequest(resp, req, ar)
}
if !resp.IsError {
t.Fatalf("Response should be an error")
}
if resp.ErrorId != E_UNAUTHORIZED_CLIENT {
t.Fatalf("Incorrect error in response: %v", resp.ErrorId)
}
usedRedirectUrl, redirectErr := resp.GetRedirectUrl()
if redirectErr == nil && usedRedirectUrl == redirectUri {
t.Fatalf("Response must not redirect to the provided redirect URL for an invalid client")
}
}
func TestAuthorizeCodePKCERequired(t *testing.T) {
sconfig := NewServerConfig()
sconfig.RequirePKCEForPublicClients = true
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE}
server := NewServer(sconfig, NewTestingStorage())
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
// Public client returns an error
{
resp := server.NewResponse()
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Form = make(url.Values)
req.Form.Set("response_type", string(CODE))
req.Form.Set("state", "a")
req.Form.Set("client_id", "public-client")
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAuthorizeRequest(resp, req, ar)
}
if !resp.IsError || resp.ErrorId != "invalid_request" || strings.Contains(resp.StatusText, "code_challenge") {
t.Errorf("Expected invalid_request error describing the code_challenge required, got %#v", resp)
}
}
// Confidential client works without PKCE
{
resp := server.NewResponse()
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Form = make(url.Values)
req.Form.Set("response_type", string(CODE))
req.Form.Set("state", "a")
req.Form.Set("client_id", "1234")
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAuthorizeRequest(resp, req, ar)
}
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != REDIRECT {
t.Fatalf("Response should be a redirect")
}
if d := resp.Output["code"]; d != "1" {
t.Fatalf("Unexpected authorization code: %s", d)
}
}
}
func TestAuthorizeCodePKCEPlain(t *testing.T) {
challenge := "12345678901234567890123456789012345678901234567890"
sconfig := NewServerConfig()
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE}
server := NewServer(sconfig, NewTestingStorage())
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
resp := server.NewResponse()
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Form = make(url.Values)
req.Form.Set("response_type", string(CODE))
req.Form.Set("client_id", "1234")
req.Form.Set("state", "a")
req.Form.Set("code_challenge", challenge)
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAuthorizeRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != REDIRECT {
t.Fatalf("Response should be a redirect")
}
code, ok := resp.Output["code"].(string)
if !ok || code != "1" {
t.Fatalf("Unexpected authorization code: %s", code)
}
token, err := server.Storage.LoadAuthorize(code)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if token.CodeChallenge != challenge {
t.Errorf("Expected stored code_challenge %s, got %s", challenge, token.CodeChallenge)
}
if token.CodeChallengeMethod != "plain" {
t.Errorf("Expected stored code_challenge plain, got %s", token.CodeChallengeMethod)
}
}
func TestAuthorizeCodePKCES256(t *testing.T) {
challenge := "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
sconfig := NewServerConfig()
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE}
server := NewServer(sconfig, NewTestingStorage())
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
resp := server.NewResponse()
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Form = make(url.Values)
req.Form.Set("response_type", string(CODE))
req.Form.Set("client_id", "1234")
req.Form.Set("state", "a")
req.Form.Set("code_challenge", challenge)
req.Form.Set("code_challenge_method", "S256")
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
ar.Authorized = true
server.FinishAuthorizeRequest(resp, req, ar)
}
//fmt.Printf("%+v", resp)
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != REDIRECT {
t.Fatalf("Response should be a redirect")
}
code, ok := resp.Output["code"].(string)
if !ok || code != "1" {
t.Fatalf("Unexpected authorization code: %s", code)
}
token, err := server.Storage.LoadAuthorize(code)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if token.CodeChallenge != challenge {
t.Errorf("Expected stored code_challenge %s, got %s", challenge, token.CodeChallenge)
}
if token.CodeChallengeMethod != "S256" {
t.Errorf("Expected stored code_challenge S256, got %s", token.CodeChallengeMethod)
}
}

60
vendor/github.com/RangelReale/osin/client.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
package osin
// Client information
type Client interface {
// Client id
GetId() string
// Client secret
GetSecret() string
// Base client uri
GetRedirectUri() string
// Data to be passed to storage. Not used by the library.
GetUserData() interface{}
}
// ClientSecretMatcher is an optional interface clients can implement
// which allows them to be the one to determine if a secret matches.
// If a Client implements ClientSecretMatcher, the framework will never call GetSecret
type ClientSecretMatcher interface {
// SecretMatches returns true if the given secret matches
ClientSecretMatches(secret string) bool
}
// DefaultClient stores all data in struct variables
type DefaultClient struct {
Id string
Secret string
RedirectUri string
UserData interface{}
}
func (d *DefaultClient) GetId() string {
return d.Id
}
func (d *DefaultClient) GetSecret() string {
return d.Secret
}
func (d *DefaultClient) GetRedirectUri() string {
return d.RedirectUri
}
func (d *DefaultClient) GetUserData() interface{} {
return d.UserData
}
// Implement the ClientSecretMatcher interface
func (d *DefaultClient) ClientSecretMatches(secret string) bool {
return d.Secret == secret
}
func (d *DefaultClient) CopyFrom(client Client) {
d.Id = client.GetId()
d.Secret = client.GetSecret()
d.RedirectUri = client.GetRedirectUri()
d.UserData = client.GetUserData()
}

18
vendor/github.com/RangelReale/osin/client_test.go generated vendored Normal file
View File

@@ -0,0 +1,18 @@
package osin
import (
"testing"
)
func TestClientIntfUserData(t *testing.T) {
c := &DefaultClient{
UserData: make(map[string]interface{}),
}
// check if the interface{} returned from the method is a reference
c.GetUserData().(map[string]interface{})["test"] = "none"
if _, ok := c.GetUserData().(map[string]interface{})["test"]; !ok {
t.Error("Returned interface is not a reference")
}
}

82
vendor/github.com/RangelReale/osin/config.go generated vendored Normal file
View File

@@ -0,0 +1,82 @@
package osin
// AllowedAuthorizeType is a collection of allowed auth request types
type AllowedAuthorizeType []AuthorizeRequestType
// Exists returns true if the auth type exists in the list
func (t AllowedAuthorizeType) Exists(rt AuthorizeRequestType) bool {
for _, k := range t {
if k == rt {
return true
}
}
return false
}
// AllowedAccessType is a collection of allowed access request types
type AllowedAccessType []AccessRequestType
// Exists returns true if the access type exists in the list
func (t AllowedAccessType) Exists(rt AccessRequestType) bool {
for _, k := range t {
if k == rt {
return true
}
}
return false
}
// ServerConfig contains server configuration information
type ServerConfig struct {
// Authorization token expiration in seconds (default 5 minutes)
AuthorizationExpiration int32
// Access token expiration in seconds (default 1 hour)
AccessExpiration int32
// Token type to return
TokenType string
// List of allowed authorize types (only CODE by default)
AllowedAuthorizeTypes AllowedAuthorizeType
// List of allowed access types (only AUTHORIZATION_CODE by default)
AllowedAccessTypes AllowedAccessType
// HTTP status code to return for errors - default 200
// Only used if response was created from server
ErrorStatusCode int
// If true allows client secret also in params, else only in
// Authorization header - default false
AllowClientSecretInParams bool
// If true allows access request using GET, else only POST - default false
AllowGetAccessRequest bool
// Require PKCE for code flows for public OAuth clients - default false
RequirePKCEForPublicClients bool
// Separator to support multiple URIs in Client.GetRedirectUri().
// If blank (the default), don't allow multiple URIs.
RedirectUriSeparator string
// RetainTokenAfter Refresh allows the server to retain the access and
// refresh token for re-use - default false
RetainTokenAfterRefresh bool
}
// NewServerConfig returns a new ServerConfig with default configuration
func NewServerConfig() *ServerConfig {
return &ServerConfig{
AuthorizationExpiration: 250,
AccessExpiration: 3600,
TokenType: "Bearer",
AllowedAuthorizeTypes: AllowedAuthorizeType{CODE},
AllowedAccessTypes: AllowedAccessType{AUTHORIZATION_CODE},
ErrorStatusCode: 200,
AllowClientSecretInParams: false,
AllowGetAccessRequest: false,
RetainTokenAfterRefresh: false,
}
}

52
vendor/github.com/RangelReale/osin/error.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
package osin
type DefaultErrorId string
const (
E_INVALID_REQUEST string = "invalid_request"
E_UNAUTHORIZED_CLIENT = "unauthorized_client"
E_ACCESS_DENIED = "access_denied"
E_UNSUPPORTED_RESPONSE_TYPE = "unsupported_response_type"
E_INVALID_SCOPE = "invalid_scope"
E_SERVER_ERROR = "server_error"
E_TEMPORARILY_UNAVAILABLE = "temporarily_unavailable"
E_UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type"
E_INVALID_GRANT = "invalid_grant"
E_INVALID_CLIENT = "invalid_client"
)
var (
deferror *DefaultErrors = NewDefaultErrors()
)
// Default errors and messages
type DefaultErrors struct {
errormap map[string]string
}
// NewDefaultErrors initializes OAuth2 error codes and descriptions.
// http://tools.ietf.org/html/rfc6749#section-4.1.2.1
// http://tools.ietf.org/html/rfc6749#section-4.2.2.1
// http://tools.ietf.org/html/rfc6749#section-5.2
// http://tools.ietf.org/html/rfc6749#section-7.2
func NewDefaultErrors() *DefaultErrors {
r := &DefaultErrors{errormap: make(map[string]string)}
r.errormap[E_INVALID_REQUEST] = "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed."
r.errormap[E_UNAUTHORIZED_CLIENT] = "The client is not authorized to request a token using this method."
r.errormap[E_ACCESS_DENIED] = "The resource owner or authorization server denied the request."
r.errormap[E_UNSUPPORTED_RESPONSE_TYPE] = "The authorization server does not support obtaining a token using this method."
r.errormap[E_INVALID_SCOPE] = "The requested scope is invalid, unknown, or malformed."
r.errormap[E_SERVER_ERROR] = "The authorization server encountered an unexpected condition that prevented it from fulfilling the request."
r.errormap[E_TEMPORARILY_UNAVAILABLE] = "The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server."
r.errormap[E_UNSUPPORTED_GRANT_TYPE] = "The authorization grant type is not supported by the authorization server."
r.errormap[E_INVALID_GRANT] = "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
r.errormap[E_INVALID_CLIENT] = "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)."
return r
}
func (e *DefaultErrors) Get(id string) string {
if m, ok := e.errormap[id]; ok {
return m
}
return id
}

View File

@@ -0,0 +1,411 @@
package main
// Open url in browser:
// http://localhost:14000/app
import (
"fmt"
"github.com/RangelReale/osin"
"github.com/RangelReale/osin/example"
"net/http"
"net/url"
)
func main() {
sconfig := osin.NewServerConfig()
sconfig.AllowedAuthorizeTypes = osin.AllowedAuthorizeType{osin.CODE, osin.TOKEN}
sconfig.AllowedAccessTypes = osin.AllowedAccessType{osin.AUTHORIZATION_CODE,
osin.REFRESH_TOKEN, osin.PASSWORD, osin.CLIENT_CREDENTIALS, osin.ASSERTION}
sconfig.AllowGetAccessRequest = true
sconfig.AllowClientSecretInParams = true
server := osin.NewServer(sconfig, example.NewTestStorage())
// Authorization code endpoint
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
if !example.HandleLoginPage(ar, w, r) {
return
}
ar.UserData = struct{ Login string }{Login: "test"}
ar.Authorized = true
server.FinishAuthorizeRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
if !resp.IsError {
resp.Output["custom_parameter"] = 187723
}
osin.OutputJSON(resp, w, r)
})
// Access token endpoint
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAccessRequest(resp, r); ar != nil {
switch ar.Type {
case osin.AUTHORIZATION_CODE:
ar.Authorized = true
case osin.REFRESH_TOKEN:
ar.Authorized = true
case osin.PASSWORD:
if ar.Username == "test" && ar.Password == "test" {
ar.Authorized = true
}
case osin.CLIENT_CREDENTIALS:
ar.Authorized = true
case osin.ASSERTION:
if ar.AssertionType == "urn:osin.example.complete" && ar.Assertion == "osin.data" {
ar.Authorized = true
}
}
server.FinishAccessRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
if !resp.IsError {
resp.Output["custom_parameter"] = 19923
}
osin.OutputJSON(resp, w, r)
})
// Information endpoint
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ir := server.HandleInfoRequest(resp, r); ir != nil {
server.FinishInfoRequest(resp, r, ir)
}
osin.OutputJSON(resp, w, r)
})
// Application home endpoint
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("<html><body>"))
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Code</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=token&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Implicit</a><br/>", url.QueryEscape("http://localhost:14000/appauth/token"))))
w.Write([]byte(fmt.Sprintf("<a href=\"/appauth/password\">Password</a><br/>")))
w.Write([]byte(fmt.Sprintf("<a href=\"/appauth/client_credentials\">Client Credentials</a><br/>")))
w.Write([]byte(fmt.Sprintf("<a href=\"/appauth/assertion\">Assertion</a><br/>")))
w.Write([]byte("</body></html>"))
})
// Application destination - CODE
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
code := r.Form.Get("code")
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - CODE<br/>"))
defer w.Write([]byte("</body></html>"))
if code == "" {
w.Write([]byte("Nothing to do"))
return
}
jr := make(map[string]interface{})
// build access code url
aurl := fmt.Sprintf("/token?grant_type=authorization_code&client_id=1234&client_secret=aabbccdd&state=xyz&redirect_uri=%s&code=%s",
url.QueryEscape("http://localhost:14000/appauth/code"), url.QueryEscape(code))
// if parse, download and parse json
if r.Form.Get("doparse") == "1" {
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
&osin.BasicAuth{"1234", "aabbccdd"}, jr)
if err != nil {
w.Write([]byte(err.Error()))
w.Write([]byte("<br/>"))
}
}
// show json error
if erd, ok := jr["error"]; ok {
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
}
// show json access token
if at, ok := jr["access_token"]; ok {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
// output links
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Goto Token URL</a><br/>", aurl)))
cururl := *r.URL
curq := cururl.Query()
curq.Add("doparse", "1")
cururl.RawQuery = curq.Encode()
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
if rt, ok := jr["refresh_token"]; ok {
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
}
if at, ok := jr["access_token"]; ok {
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
}
})
// Application destination - TOKEN
http.HandleFunc("/appauth/token", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - TOKEN<br/>"))
w.Write([]byte("Response data in fragment - not acessible via server - Nothing to do"))
w.Write([]byte("</body></html>"))
})
// Application destination - PASSWORD
http.HandleFunc("/appauth/password", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - PASSWORD<br/>"))
jr := make(map[string]interface{})
// build access code url
aurl := fmt.Sprintf("/token?grant_type=password&scope=everything&username=%s&password=%s",
"test", "test")
// download token
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
if err != nil {
w.Write([]byte(err.Error()))
w.Write([]byte("<br/>"))
}
// show json error
if erd, ok := jr["error"]; ok {
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
}
// show json access token
if at, ok := jr["access_token"]; ok {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
if rt, ok := jr["refresh_token"]; ok {
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
}
if at, ok := jr["access_token"]; ok {
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
}
w.Write([]byte("</body></html>"))
})
// Application destination - CLIENT_CREDENTIALS
http.HandleFunc("/appauth/client_credentials", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - CLIENT CREDENTIALS<br/>"))
jr := make(map[string]interface{})
// build access code url
aurl := fmt.Sprintf("/token?grant_type=client_credentials")
// download token
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
if err != nil {
w.Write([]byte(err.Error()))
w.Write([]byte("<br/>"))
}
// show json error
if erd, ok := jr["error"]; ok {
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
}
// show json access token
if at, ok := jr["access_token"]; ok {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
if rt, ok := jr["refresh_token"]; ok {
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
}
if at, ok := jr["access_token"]; ok {
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
}
w.Write([]byte("</body></html>"))
})
// Application destination - ASSERTION
http.HandleFunc("/appauth/assertion", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - ASSERTION<br/>"))
jr := make(map[string]interface{})
// build access code url
aurl := fmt.Sprintf("/token?grant_type=assertion&assertion_type=urn:osin.example.complete&assertion=osin.data")
// download token
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
if err != nil {
w.Write([]byte(err.Error()))
w.Write([]byte("<br/>"))
}
// show json error
if erd, ok := jr["error"]; ok {
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
}
// show json access token
if at, ok := jr["access_token"]; ok {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
if rt, ok := jr["refresh_token"]; ok {
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
}
if at, ok := jr["access_token"]; ok {
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
}
w.Write([]byte("</body></html>"))
})
// Application destination - REFRESH
http.HandleFunc("/appauth/refresh", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - REFRESH<br/>"))
defer w.Write([]byte("</body></html>"))
code := r.Form.Get("code")
if code == "" {
w.Write([]byte("Nothing to do"))
return
}
jr := make(map[string]interface{})
// build access code url
aurl := fmt.Sprintf("/token?grant_type=refresh_token&refresh_token=%s", url.QueryEscape(code))
// download token
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
if err != nil {
w.Write([]byte(err.Error()))
w.Write([]byte("<br/>"))
}
// show json error
if erd, ok := jr["error"]; ok {
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
}
// show json access token
if at, ok := jr["access_token"]; ok {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
if rt, ok := jr["refresh_token"]; ok {
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
}
if at, ok := jr["access_token"]; ok {
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
}
})
// Application destination - INFO
http.HandleFunc("/appauth/info", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - INFO<br/>"))
defer w.Write([]byte("</body></html>"))
code := r.Form.Get("code")
if code == "" {
w.Write([]byte("Nothing to do"))
return
}
jr := make(map[string]interface{})
// build access code url
aurl := fmt.Sprintf("/info?code=%s", url.QueryEscape(code))
// download token
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
if err != nil {
w.Write([]byte(err.Error()))
w.Write([]byte("<br/>"))
}
// show json error
if erd, ok := jr["error"]; ok {
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
}
// show json access token
if at, ok := jr["access_token"]; ok {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
if rt, ok := jr["refresh_token"]; ok {
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
}
})
http.ListenAndServe(":14000", nil)
}

View File

@@ -0,0 +1,130 @@
package main
// Use golang.org/x/oauth2 client to test
// Open url in browser:
// http://localhost:14000/app
import (
"fmt"
"net/http"
"github.com/RangelReale/osin"
"github.com/RangelReale/osin/example"
"golang.org/x/oauth2"
)
func main() {
config := osin.NewServerConfig()
// goauth2 checks errors using status codes
config.ErrorStatusCode = 401
server := osin.NewServer(config, example.NewTestStorage())
client := &oauth2.Config{
ClientID: "1234",
ClientSecret: "aabbccdd",
Endpoint: oauth2.Endpoint{
AuthURL: "http://localhost:14000/authorize",
TokenURL: "http://localhost:14000/token",
},
RedirectURL: "http://localhost:14000/appauth/code",
}
// Authorization code endpoint
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
if !example.HandleLoginPage(ar, w, r) {
return
}
ar.Authorized = true
server.FinishAuthorizeRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
})
// Access token endpoint
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAccessRequest(resp, r); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
})
// Information endpoint
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ir := server.HandleInfoRequest(resp, r); ir != nil {
server.FinishInfoRequest(resp, r, ir)
}
osin.OutputJSON(resp, w, r)
})
// Application home endpoint
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("<html><body>"))
//w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Login</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Login</a><br/>", client.AuthCodeURL(""))))
w.Write([]byte("</body></html>"))
})
// Application destination - CODE
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
code := r.Form.Get("code")
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - CODE<br/>"))
defer w.Write([]byte("</body></html>"))
if code == "" {
w.Write([]byte("Nothing to do"))
return
}
var jr *oauth2.Token
var err error
// if parse, download and parse json
if r.Form.Get("doparse") == "1" {
jr, err = client.Exchange(oauth2.NoContext, code)
if err != nil {
jr = nil
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", err)))
}
}
// show json access token
if jr != nil {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", jr.AccessToken)))
if jr.RefreshToken != "" {
w.Write([]byte(fmt.Sprintf("REFRESH TOKEN: %s<br/>\n", jr.RefreshToken)))
}
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
cururl := *r.URL
curq := cururl.Query()
curq.Add("doparse", "1")
cururl.RawQuery = curq.Encode()
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
})
http.ListenAndServe(":14000", nil)
}

57
vendor/github.com/RangelReale/osin/example/helper.go generated vendored Normal file
View File

@@ -0,0 +1,57 @@
package example
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/RangelReale/osin"
)
func HandleLoginPage(ar *osin.AuthorizeRequest, w http.ResponseWriter, r *http.Request) bool {
r.ParseForm()
if r.Method == "POST" && r.Form.Get("login") == "test" && r.Form.Get("password") == "test" {
return true
}
w.Write([]byte("<html><body>"))
w.Write([]byte(fmt.Sprintf("LOGIN %s (use test/test)<br/>", ar.Client.GetId())))
w.Write([]byte(fmt.Sprintf("<form action=\"/authorize?%s\" method=\"POST\">", r.URL.RawQuery)))
w.Write([]byte("Login: <input type=\"text\" name=\"login\" /><br/>"))
w.Write([]byte("Password: <input type=\"password\" name=\"password\" /><br/>"))
w.Write([]byte("<input type=\"submit\"/>"))
w.Write([]byte("</form>"))
w.Write([]byte("</body></html>"))
return false
}
func DownloadAccessToken(url string, auth *osin.BasicAuth, output map[string]interface{}) error {
// download access token
preq, err := http.NewRequest("POST", url, nil)
if err != nil {
return err
}
if auth != nil {
preq.SetBasicAuth(auth.Username, auth.Password)
}
pclient := &http.Client{}
presp, err := pclient.Do(preq)
if err != nil {
return err
}
if presp.StatusCode != 200 {
return errors.New("Invalid status code")
}
jdec := json.NewDecoder(presp.Body)
err = jdec.Decode(&output)
return err
}

View File

@@ -0,0 +1,215 @@
package main
// Open url in browser:
// http://localhost:14000/app
import (
"crypto/rsa"
"fmt"
"net/http"
"net/url"
"github.com/RangelReale/osin"
"github.com/RangelReale/osin/example"
jwt "github.com/dgrijalva/jwt-go"
)
// JWT access token generator
type AccessTokenGenJWT struct {
PrivateKey *rsa.PrivateKey
PublicKey *rsa.PublicKey
}
func (c *AccessTokenGenJWT) GenerateAccessToken(data *osin.AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) {
// generate JWT access token
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
"cid": data.Client.GetId(),
"exp": data.ExpireAt().Unix(),
})
accesstoken, err = token.SignedString(c.PrivateKey)
if err != nil {
return "", "", err
}
if !generaterefresh {
return
}
// generate JWT refresh token
token = jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
"cid": data.Client.GetId(),
})
refreshtoken, err = token.SignedString(c.PrivateKey)
if err != nil {
return "", "", err
}
return
}
func main() {
server := osin.NewServer(osin.NewServerConfig(), example.NewTestStorage())
var err error
var accessTokenGenJWT AccessTokenGenJWT
if accessTokenGenJWT.PrivateKey, err = jwt.ParseRSAPrivateKeyFromPEM(privatekeyPEM); err != nil {
fmt.Printf("ERROR: %s\n", err)
return
}
if accessTokenGenJWT.PublicKey, err = jwt.ParseRSAPublicKeyFromPEM(publickeyPEM); err != nil {
fmt.Printf("ERROR: %s\n", err)
return
}
server.AccessTokenGen = &accessTokenGenJWT
// Authorization code endpoint
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
if !example.HandleLoginPage(ar, w, r) {
return
}
ar.Authorized = true
server.FinishAuthorizeRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
})
// Access token endpoint
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAccessRequest(resp, r); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
})
// Information endpoint
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ir := server.HandleInfoRequest(resp, r); ir != nil {
server.FinishInfoRequest(resp, r, ir)
}
osin.OutputJSON(resp, w, r)
})
// Application home endpoint
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("<html><body>"))
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Login</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
w.Write([]byte("</body></html>"))
})
// Application destination - CODE
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
code := r.Form.Get("code")
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - CODE<br/>"))
defer w.Write([]byte("</body></html>"))
if code == "" {
w.Write([]byte("Nothing to do"))
return
}
jr := make(map[string]interface{})
// build access code url
aurl := fmt.Sprintf("/token?grant_type=authorization_code&client_id=1234&state=xyz&redirect_uri=%s&code=%s",
url.QueryEscape("http://localhost:14000/appauth/code"), url.QueryEscape(code))
// if parse, download and parse json
if r.Form.Get("doparse") == "1" {
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
&osin.BasicAuth{"1234", "aabbccdd"}, jr)
if err != nil {
w.Write([]byte(err.Error()))
w.Write([]byte("<br/>"))
}
}
// show json error
if erd, ok := jr["error"]; ok {
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
}
// show json access token
if at, ok := jr["access_token"]; ok {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
// output links
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Goto Token URL</a><br/>", aurl)))
cururl := *r.URL
curq := cururl.Query()
curq.Add("doparse", "1")
cururl.RawQuery = curq.Encode()
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
})
http.ListenAndServe(":14000", nil)
}
var (
privatekeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn
SgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i
cqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC
PUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR
ap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA
Rdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3
n6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy
MaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9
POIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE
KdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM
IvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn
FcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY
mEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj
FuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U
I5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs
2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn
/iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT
OvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86
EunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+
hR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0
4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb
mDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry
eBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3
CKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+
9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq
-----END RSA PRIVATE KEY-----`)
publickeyPEM = []byte(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4f5wg5l2hKsTeNem/V41
fGnJm6gOdrj8ym3rFkEU/wT8RDtnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7
mCpz9Er5qLaMXJwZxzHzAahlfA0icqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBp
HssPnpYGIn20ZZuNlX2BrClciHhCPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2
XrHhR+1DcKJzQBSTAGnpYVaqpsARap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3b
ODIRe1AuTyHceAbewn8b462yEWKARdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy
7wIDAQAB
-----END PUBLIC KEY-----`)
)

View File

@@ -0,0 +1,273 @@
/*
An example of adding OpenID Connect support to osin.
*/
package main
import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"time"
"github.com/RangelReale/osin"
"github.com/RangelReale/osin/example"
"gopkg.in/square/go-jose.v1"
)
var (
issuer = "http://127.0.0.1:14001"
server = osin.NewServer(osin.NewServerConfig(), example.NewTestStorage())
jwtSigner jose.Signer
publicKeys *jose.JsonWebKeySet
)
func main() {
// Load signing key.
block, _ := pem.Decode(privateKeyBytes)
if block == nil {
log.Fatalf("no private key found")
}
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
log.Fatalf("failed to parse key: %v", err)
}
// Configure jwtSigner and public keys.
privateKey := &jose.JsonWebKey{
Key: key,
Algorithm: "RS256",
Use: "sig",
KeyID: "1", // KeyID should use the key thumbprint.
}
jwtSigner, err = jose.NewSigner(jose.RS256, privateKey)
if err != nil {
log.Fatalf("failed to create jwtSigner: %v", err)
}
publicKeys = &jose.JsonWebKeySet{
Keys: []jose.JsonWebKey{
jose.JsonWebKey{Key: &key.PublicKey,
Algorithm: "RS256",
Use: "sig",
KeyID: "1",
},
},
}
// Register the four manditory OpenID Connect endpoints: discovery, public keys, auth, and token.
http.HandleFunc("/.well-known/openid-configuration", handleDiscovery)
http.HandleFunc("/publickeys", handlePublicKeys)
http.HandleFunc("/authorize", handleAuthorization)
http.HandleFunc("/token", handleToken)
log.Fatal(http.ListenAndServe("127.0.0.1:14001", nil))
}
// The ID Token represents a JWT passed to the client as part of the token response.
//
// https://openid.net/specs/openid-connect-core-1_0.html#IDToken
type IDToken struct {
Issuer string `json:"iss"`
UserID string `json:"sub"`
ClientID string `json:"aud"`
Expiration int64 `json:"exp"`
IssuedAt int64 `json:"iat"`
Nonce string `json:"nonce,omitempty"` // Non-manditory fields MUST be "omitempty"
// Custom claims supported by this server.
//
// See: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
Email string `json:"email,omitempty"`
EmailVerified *bool `json:"email_verified,omitempty"`
Name string `json:"name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
GivenName string `json:"given_name,omitempty"`
Locale string `json:"locale,omitempty"`
}
// handleDiscovery returns the OpenID Connect discovery object, allowing clients
// to discover OAuth2 resources.
func handleDiscovery(w http.ResponseWriter, r *http.Request) {
// For other example see: https://accounts.google.com/.well-known/openid-configuration
data := map[string]interface{}{
"issuer": issuer,
"authorization_endpoint": issuer + "/authorize",
"token_endpoint": issuer + "/token",
"jwks_uri": issuer + "/publickeys",
"response_types_supported": []string{"code"},
"subject_types_supported": []string{"public"},
"id_token_signing_alg_values_supported": []string{"RS256"},
"scopes_supported": []string{"openid", "email", "profile"},
"token_endpoint_auth_methods_supported": []string{"client_secret_basic"},
"claims_supported": []string{
"aud", "email", "email_verified", "exp",
"family_name", "given_name", "iat", "iss",
"locale", "name", "sub",
},
}
raw, err := json.MarshalIndent(data, "", " ")
if err != nil {
log.Printf("failed to marshal data: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Length", strconv.Itoa(len(raw)))
w.Write(raw)
}
// handlePublicKeys publishes the public part of this server's signing keys.
// This allows clients to verify the signature of ID Tokens.
func handlePublicKeys(w http.ResponseWriter, r *http.Request) {
raw, err := json.MarshalIndent(publicKeys, "", " ")
if err != nil {
log.Printf("failed to marshal data: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Length", strconv.Itoa(len(raw)))
w.Write(raw)
}
func handleAuthorization(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
if !example.HandleLoginPage(ar, w, r) {
return
}
ar.Authorized = true
scopes := make(map[string]bool)
for _, s := range strings.Fields(ar.Scope) {
scopes[s] = true
}
// If the "openid" connect scope is specified, attach an ID Token to the
// authorization response.
//
// The ID Token will be serialized and signed during the code for token exchange.
if scopes["openid"] {
// These values would be tied to the end user authorizing the client.
now := time.Now()
idToken := IDToken{
Issuer: issuer,
UserID: "id-of-test-user",
ClientID: ar.Client.GetId(),
Expiration: now.Add(time.Hour).Unix(),
IssuedAt: now.Unix(),
Nonce: r.URL.Query().Get("nonce"),
}
if scopes["profile"] {
idToken.Name = "Jane Doe"
idToken.GivenName = "Jane"
idToken.FamilyName = "Doe"
idToken.Locale = "us"
}
if scopes["email"] {
t := true
idToken.Email = "jane.doe@example.com"
idToken.EmailVerified = &t
}
// NOTE: The storage must be able to encode and decode this object.
ar.UserData = &idToken
}
server.FinishAuthorizeRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
log.Printf("internal error: %v", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
}
func handleToken(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAccessRequest(resp, r); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, r, ar)
// If an ID Token was encoded as the UserData, serialize and sign it.
if idToken, ok := ar.UserData.(*IDToken); ok && idToken != nil {
encodeIDToken(resp, idToken, jwtSigner)
}
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
}
// encodeIDToken serializes and signs an ID Token then adds a field to the token response.
func encodeIDToken(resp *osin.Response, idToken *IDToken, singer jose.Signer) {
resp.InternalError = func() error {
payload, err := json.Marshal(idToken)
if err != nil {
return fmt.Errorf("failed to marshal token: %v", err)
}
jws, err := jwtSigner.Sign(payload)
if err != nil {
return fmt.Errorf("failed to sign token: %v", err)
}
raw, err := jws.CompactSerialize()
if err != nil {
return fmt.Errorf("failed to serialize token: %v", err)
}
resp.Output["id_token"] = raw
return nil
}()
// Record errors as internal server errors.
if resp.InternalError != nil {
resp.IsError = true
resp.ErrorId = osin.E_SERVER_ERROR
}
}
var (
privateKeyBytes = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn
SgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i
cqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC
PUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR
ap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA
Rdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3
n6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy
MaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9
POIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE
KdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM
IvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn
FcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY
mEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj
FuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U
I5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs
2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn
/iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT
OvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86
EunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+
hR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0
4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb
mDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry
eBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3
CKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+
9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq
-----END RSA PRIVATE KEY-----`)
)

View File

@@ -0,0 +1,127 @@
package main
// Use github.com/RangelReale/osincli client to test
// Open url in browser:
// http://localhost:14001
import (
"fmt"
"github.com/RangelReale/osin"
"github.com/RangelReale/osin/example"
"github.com/RangelReale/osincli"
"net/http"
)
func main() {
// create http muxes
serverhttp := http.NewServeMux()
clienthttp := http.NewServeMux()
// create server
config := osin.NewServerConfig()
sstorage := example.NewTestStorage()
sstorage.SetClient("1234", &osin.DefaultClient{
Id: "1234",
Secret: "aabbccdd",
RedirectUri: "http://localhost:14001/appauth",
})
server := osin.NewServer(config, sstorage)
// create client
cliconfig := &osincli.ClientConfig{
ClientId: "1234",
ClientSecret: "aabbccdd",
AuthorizeUrl: "http://localhost:14000/authorize",
TokenUrl: "http://localhost:14000/token",
RedirectUrl: "http://localhost:14001/appauth",
}
client, err := osincli.NewClient(cliconfig)
if err != nil {
panic(err)
}
// create a new request to generate the url
areq := client.NewAuthorizeRequest(osincli.CODE)
// SERVER
// Authorization code endpoint
serverhttp.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
if !example.HandleLoginPage(ar, w, r) {
return
}
ar.Authorized = true
server.FinishAuthorizeRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
})
// Access token endpoint
serverhttp.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAccessRequest(resp, r); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
})
// Information endpoint
serverhttp.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ir := server.HandleInfoRequest(resp, r); ir != nil {
server.FinishInfoRequest(resp, r, ir)
}
osin.OutputJSON(resp, w, r)
})
// CLIENT
// Home
clienthttp.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
u := areq.GetAuthorizeUrl()
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Login</a>", u.String())))
})
// Auth endpoint
clienthttp.HandleFunc("/appauth", func(w http.ResponseWriter, r *http.Request) {
// parse a token request
areqdata, err := areq.HandleRequest(r)
if err != nil {
w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err)))
return
}
treq := client.NewAccessRequest(osincli.AUTHORIZATION_CODE, areqdata)
// show access request url (for debugging only)
u2 := treq.GetTokenUrl()
w.Write([]byte(fmt.Sprintf("Access token URL: %s\n", u2.String())))
// exchange the authorize token for the access token
ad, err := treq.GetToken()
if err != nil {
w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err)))
return
}
w.Write([]byte(fmt.Sprintf("Access token: %+v\n", ad)))
})
go http.ListenAndServe(":14001", clienthttp)
http.ListenAndServe(":14000", serverhttp)
}

View File

@@ -0,0 +1,126 @@
package main
// Open url in browser:
// http://localhost:14000/app
import (
"fmt"
"github.com/RangelReale/osin"
"github.com/RangelReale/osin/example"
"net/http"
"net/url"
)
func main() {
cfg := osin.NewServerConfig()
cfg.AllowGetAccessRequest = true
cfg.AllowClientSecretInParams = true
server := osin.NewServer(cfg, example.NewTestStorage())
// Authorization code endpoint
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
if !example.HandleLoginPage(ar, w, r) {
return
}
ar.Authorized = true
server.FinishAuthorizeRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
})
// Access token endpoint
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ar := server.HandleAccessRequest(resp, r); ar != nil {
ar.Authorized = true
server.FinishAccessRequest(resp, r, ar)
}
if resp.IsError && resp.InternalError != nil {
fmt.Printf("ERROR: %s\n", resp.InternalError)
}
osin.OutputJSON(resp, w, r)
})
// Information endpoint
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
resp := server.NewResponse()
defer resp.Close()
if ir := server.HandleInfoRequest(resp, r); ir != nil {
server.FinishInfoRequest(resp, r, ir)
}
osin.OutputJSON(resp, w, r)
})
// Application home endpoint
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("<html><body>"))
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Login</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
w.Write([]byte("</body></html>"))
})
// Application destination - CODE
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
code := r.Form.Get("code")
w.Write([]byte("<html><body>"))
w.Write([]byte("APP AUTH - CODE<br/>"))
defer w.Write([]byte("</body></html>"))
if code == "" {
w.Write([]byte("Nothing to do"))
return
}
jr := make(map[string]interface{})
// build access code url
aurl := fmt.Sprintf("/token?grant_type=authorization_code&client_id=1234&client_secret=aabbccdd&state=xyz&redirect_uri=%s&code=%s",
url.QueryEscape("http://localhost:14000/appauth/code"), url.QueryEscape(code))
// if parse, download and parse json
if r.Form.Get("doparse") == "1" {
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
&osin.BasicAuth{"1234", "aabbccdd"}, jr)
if err != nil {
w.Write([]byte(err.Error()))
w.Write([]byte("<br/>"))
}
}
// show json error
if erd, ok := jr["error"]; ok {
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
}
// show json access token
if at, ok := jr["access_token"]; ok {
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
}
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
// output links
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Goto Token URL</a><br/>", aurl)))
cururl := *r.URL
curq := cururl.Query()
curq.Add("doparse", "1")
cururl.RawQuery = curq.Encode()
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
})
http.ListenAndServe(":14000", nil)
}

View File

@@ -0,0 +1,108 @@
package example
import (
"fmt"
"github.com/RangelReale/osin"
)
type TestStorage struct {
clients map[string]osin.Client
authorize map[string]*osin.AuthorizeData
access map[string]*osin.AccessData
refresh map[string]string
}
func NewTestStorage() *TestStorage {
r := &TestStorage{
clients: make(map[string]osin.Client),
authorize: make(map[string]*osin.AuthorizeData),
access: make(map[string]*osin.AccessData),
refresh: make(map[string]string),
}
r.clients["1234"] = &osin.DefaultClient{
Id: "1234",
Secret: "aabbccdd",
RedirectUri: "http://localhost:14000/appauth",
}
return r
}
func (s *TestStorage) Clone() osin.Storage {
return s
}
func (s *TestStorage) Close() {
}
func (s *TestStorage) GetClient(id string) (osin.Client, error) {
fmt.Printf("GetClient: %s\n", id)
if c, ok := s.clients[id]; ok {
return c, nil
}
return nil, osin.ErrNotFound
}
func (s *TestStorage) SetClient(id string, client osin.Client) error {
fmt.Printf("SetClient: %s\n", id)
s.clients[id] = client
return nil
}
func (s *TestStorage) SaveAuthorize(data *osin.AuthorizeData) error {
fmt.Printf("SaveAuthorize: %s\n", data.Code)
s.authorize[data.Code] = data
return nil
}
func (s *TestStorage) LoadAuthorize(code string) (*osin.AuthorizeData, error) {
fmt.Printf("LoadAuthorize: %s\n", code)
if d, ok := s.authorize[code]; ok {
return d, nil
}
return nil, osin.ErrNotFound
}
func (s *TestStorage) RemoveAuthorize(code string) error {
fmt.Printf("RemoveAuthorize: %s\n", code)
delete(s.authorize, code)
return nil
}
func (s *TestStorage) SaveAccess(data *osin.AccessData) error {
fmt.Printf("SaveAccess: %s\n", data.AccessToken)
s.access[data.AccessToken] = data
if data.RefreshToken != "" {
s.refresh[data.RefreshToken] = data.AccessToken
}
return nil
}
func (s *TestStorage) LoadAccess(code string) (*osin.AccessData, error) {
fmt.Printf("LoadAccess: %s\n", code)
if d, ok := s.access[code]; ok {
return d, nil
}
return nil, osin.ErrNotFound
}
func (s *TestStorage) RemoveAccess(code string) error {
fmt.Printf("RemoveAccess: %s\n", code)
delete(s.access, code)
return nil
}
func (s *TestStorage) LoadRefresh(code string) (*osin.AccessData, error) {
fmt.Printf("LoadRefresh: %s\n", code)
if d, ok := s.refresh[code]; ok {
return s.LoadAccess(d)
}
return nil, osin.ErrNotFound
}
func (s *TestStorage) RemoveRefresh(code string) error {
fmt.Printf("RemoveRefresh: %s\n", code)
delete(s.refresh, code)
return nil
}

81
vendor/github.com/RangelReale/osin/info.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
package osin
import (
"net/http"
"time"
)
// InfoRequest is a request for information about some AccessData
type InfoRequest struct {
Code string // Code to look up
AccessData *AccessData // AccessData associated with Code
}
// HandleInfoRequest is an http.HandlerFunc for server information
// NOT an RFC specification.
func (s *Server) HandleInfoRequest(w *Response, r *http.Request) *InfoRequest {
r.ParseForm()
bearer := CheckBearerAuth(r)
if bearer == nil {
w.SetError(E_INVALID_REQUEST, "")
return nil
}
// generate info request
ret := &InfoRequest{
Code: bearer.Code,
}
if ret.Code == "" {
w.SetError(E_INVALID_REQUEST, "")
return nil
}
var err error
// load access data
ret.AccessData, err = w.Storage.LoadAccess(ret.Code)
if err != nil {
w.SetError(E_INVALID_REQUEST, "")
w.InternalError = err
return nil
}
if ret.AccessData == nil {
w.SetError(E_INVALID_REQUEST, "")
return nil
}
if ret.AccessData.Client == nil {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if ret.AccessData.Client.GetRedirectUri() == "" {
w.SetError(E_UNAUTHORIZED_CLIENT, "")
return nil
}
if ret.AccessData.IsExpiredAt(s.Now()) {
w.SetError(E_INVALID_GRANT, "")
return nil
}
return ret
}
// FinishInfoRequest finalizes the request handled by HandleInfoRequest
func (s *Server) FinishInfoRequest(w *Response, r *http.Request, ir *InfoRequest) {
// don't process if is already an error
if w.IsError {
return
}
// output data
w.Output["client_id"] = ir.AccessData.Client.GetId()
w.Output["access_token"] = ir.AccessData.AccessToken
w.Output["token_type"] = s.Config.TokenType
w.Output["expires_in"] = ir.AccessData.CreatedAt.Add(time.Duration(ir.AccessData.ExpiresIn)*time.Second).Sub(s.Now()) / time.Second
if ir.AccessData.RefreshToken != "" {
w.Output["refresh_token"] = ir.AccessData.RefreshToken
}
if ir.AccessData.Scope != "" {
w.Output["scope"] = ir.AccessData.Scope
}
}

72
vendor/github.com/RangelReale/osin/info_test.go generated vendored Normal file
View File

@@ -0,0 +1,72 @@
package osin
import (
"net/http"
"net/url"
"testing"
)
func TestInfo(t *testing.T) {
sconfig := NewServerConfig()
server := NewServer(sconfig, NewTestingStorage())
resp := server.NewResponse()
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Form = make(url.Values)
req.Form.Set("code", "9999")
if ar := server.HandleInfoRequest(resp, req); ar != nil {
server.FinishInfoRequest(resp, req, ar)
}
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != DATA {
t.Fatalf("Response should be data")
}
if d := resp.Output["access_token"]; d != "9999" {
t.Fatalf("Unexpected authorization code: %s", d)
}
}
func TestInfoWhenCodeIsOnHeader(t *testing.T) {
sconfig := NewServerConfig()
server := NewServer(sconfig, NewTestingStorage())
resp := server.NewResponse()
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
req.Header.Set("Authorization", "Bearer 9999")
if ar := server.HandleInfoRequest(resp, req); ar != nil {
server.FinishInfoRequest(resp, req, ar)
}
if resp.IsError && resp.InternalError != nil {
t.Fatalf("Error in response: %s", resp.InternalError)
}
if resp.IsError {
t.Fatalf("Should not be an error")
}
if resp.Type != DATA {
t.Fatalf("Response should be data")
}
if d := resp.Output["access_token"]; d != "9999" {
t.Fatalf("Unexpected authorization code: %s", d)
}
}

151
vendor/github.com/RangelReale/osin/response.go generated vendored Normal file
View File

@@ -0,0 +1,151 @@
package osin
import (
"errors"
"fmt"
"net/http"
"net/url"
)
// Data for response output
type ResponseData map[string]interface{}
// Response type enum
type ResponseType int
const (
DATA ResponseType = iota
REDIRECT
)
// Server response
type Response struct {
Type ResponseType
StatusCode int
StatusText string
ErrorStatusCode int
URL string
Output ResponseData
Headers http.Header
IsError bool
ErrorId string
InternalError error
RedirectInFragment bool
// Storage to use in this response - required
Storage Storage
}
func NewResponse(storage Storage) *Response {
r := &Response{
Type: DATA,
StatusCode: 200,
ErrorStatusCode: 200,
Output: make(ResponseData),
Headers: make(http.Header),
IsError: false,
Storage: storage.Clone(),
}
r.Headers.Add(
"Cache-Control",
"no-cache, no-store, max-age=0, must-revalidate",
)
r.Headers.Add("Pragma", "no-cache")
r.Headers.Add("Expires", "Fri, 01 Jan 1990 00:00:00 GMT")
return r
}
// SetError sets an error id and description on the Response
// state and uri are left blank
func (r *Response) SetError(id string, description string) {
r.SetErrorUri(id, description, "", "")
}
// SetErrorState sets an error id, description, and state on the Response
// uri is left blank
func (r *Response) SetErrorState(id string, description string, state string) {
r.SetErrorUri(id, description, "", state)
}
// SetErrorUri sets an error id, description, state, and uri on the Response
func (r *Response) SetErrorUri(id string, description string, uri string, state string) {
// get default error message
if description == "" {
description = deferror.Get(id)
}
// set error parameters
r.IsError = true
r.ErrorId = id
r.StatusCode = r.ErrorStatusCode
if r.StatusCode != 200 {
r.StatusText = description
} else {
r.StatusText = ""
}
r.Output = make(ResponseData) // clear output
r.Output["error"] = id
r.Output["error_description"] = description
if uri != "" {
r.Output["error_uri"] = uri
}
if state != "" {
r.Output["state"] = state
}
}
// SetRedirect changes the response to redirect to the given url
func (r *Response) SetRedirect(url string) {
// set redirect parameters
r.Type = REDIRECT
r.URL = url
}
// SetRedirectFragment sets redirect values to be passed in fragment instead of as query parameters
func (r *Response) SetRedirectFragment(f bool) {
r.RedirectInFragment = f
}
// GetRedirectUrl returns the redirect url with all query string parameters
func (r *Response) GetRedirectUrl() (string, error) {
if r.Type != REDIRECT {
return "", errors.New("Not a redirect response")
}
u, err := url.Parse(r.URL)
if err != nil {
return "", err
}
var q url.Values
if r.RedirectInFragment {
// start with empty set for fragment
q = url.Values{}
} else {
// add parameters to existing query
q = u.Query()
}
// add parameters
for n, v := range r.Output {
q.Set(n, fmt.Sprint(v))
}
// https://tools.ietf.org/html/rfc6749#section-4.2.2
// Fragment should be encoded as application/x-www-form-urlencoded (%-escaped, spaces are represented as '+')
// The stdlib URL#String() doesn't make that easy to accomplish, so build this ourselves
if r.RedirectInFragment {
u.Fragment = ""
redirectURI := u.String() + "#" + q.Encode()
return redirectURI, nil
}
// Otherwise, update the query and encode normally
u.RawQuery = q.Encode()
u.Fragment = ""
return u.String(), nil
}
func (r *Response) Close() {
r.Storage.Close()
}

39
vendor/github.com/RangelReale/osin/response_json.go generated vendored Normal file
View File

@@ -0,0 +1,39 @@
package osin
import (
"encoding/json"
"net/http"
)
// OutputJSON encodes the Response to JSON and writes to the http.ResponseWriter
func OutputJSON(rs *Response, w http.ResponseWriter, r *http.Request) error {
// Add headers
for i, k := range rs.Headers {
for _, v := range k {
w.Header().Add(i, v)
}
}
if rs.Type == REDIRECT {
// Output redirect with parameters
u, err := rs.GetRedirectUrl()
if err != nil {
return err
}
w.Header().Add("Location", u)
w.WriteHeader(302)
} else {
// set content type if the response doesn't already have one associated with it
if w.Header().Get("Content-Type") == "" {
w.Header().Set("Content-Type", "application/json")
}
w.WriteHeader(rs.StatusCode)
encoder := json.NewEncoder(w)
err := encoder.Encode(rs.Output)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,115 @@
package osin
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
func TestResponseJSON(t *testing.T) {
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
w := httptest.NewRecorder()
r := NewResponse(NewTestingStorage())
r.Output["access_token"] = "1234"
r.Output["token_type"] = "5678"
err = OutputJSON(r, w, req)
if err != nil {
t.Fatalf("Error outputting json: %s", err)
}
//fmt.Printf("%d - %s - %+v", w.Code, w.Body.String(), w.HeaderMap)
if w.Code != 200 {
t.Fatalf("Invalid response code for output: %d", w.Code)
}
if w.HeaderMap.Get("Content-Type") != "application/json" {
t.Fatalf("Result from json must be application/json")
}
// parse output json
output := make(map[string]interface{})
if err := json.Unmarshal(w.Body.Bytes(), &output); err != nil {
t.Fatalf("Could not decode output json: %s", err)
}
if d, ok := output["access_token"]; !ok || d != "1234" {
t.Fatalf("Invalid or not found output data: access_token=%s", d)
}
if d, ok := output["token_type"]; !ok || d != "5678" {
t.Fatalf("Invalid or not found output data: token_type=%s", d)
}
}
func TestErrorResponseJSON(t *testing.T) {
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
w := httptest.NewRecorder()
r := NewResponse(NewTestingStorage())
r.ErrorStatusCode = 500
r.SetError(E_INVALID_REQUEST, "")
err = OutputJSON(r, w, req)
if err != nil {
t.Fatalf("Error outputting json: %s", err)
}
//fmt.Printf("%d - %s - %+v", w.Code, w.Body.String(), w.HeaderMap)
if w.Code != 500 {
t.Fatalf("Invalid response code for error output: %d", w.Code)
}
if w.HeaderMap.Get("Content-Type") != "application/json" {
t.Fatalf("Result from json must be application/json")
}
// parse output json
output := make(map[string]interface{})
if err := json.Unmarshal(w.Body.Bytes(), &output); err != nil {
t.Fatalf("Could not decode output json: %s", err)
}
if d, ok := output["error"]; !ok || d != E_INVALID_REQUEST {
t.Fatalf("Invalid or not found output data: error=%s", d)
}
}
func TestRedirectResponseJSON(t *testing.T) {
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
if err != nil {
t.Fatal(err)
}
w := httptest.NewRecorder()
r := NewResponse(NewTestingStorage())
r.SetRedirect("http://localhost:14000")
err = OutputJSON(r, w, req)
if err != nil {
t.Fatalf("Error outputting json: %s", err)
}
//fmt.Printf("%d - %s - %+v", w.Code, w.Body.String(), w.HeaderMap)
if w.Code != 302 {
t.Fatalf("Invalid response code for redirect output: %d", w.Code)
}
if w.HeaderMap.Get("Location") != "http://localhost:14000" {
t.Fatalf("Invalid response location url: %s", w.HeaderMap.Get("Location"))
}
}

74
vendor/github.com/RangelReale/osin/response_test.go generated vendored Normal file
View File

@@ -0,0 +1,74 @@
package osin
import (
"net/url"
"strings"
"testing"
)
func TestGetRedirectUrl(t *testing.T) {
// Make sure we can round-trip state parameters containing special URL characters, both as a query param and in an encoded fragment
state := `{"then": "/index.html?a=1&b=%2B#fragment", "nonce": "014f:bff9a07c"}`
testcases := map[string]struct {
URL string
Output ResponseData
RedirectInFragment bool
ExpectedURL string
}{
"query": {
URL: "https://foo.com/path?abc=123",
Output: ResponseData{"access_token": "12345", "state": state},
ExpectedURL: "https://foo.com/path?abc=123&access_token=12345&state=%7B%22then%22%3A+%22%2Findex.html%3Fa%3D1%26b%3D%252B%23fragment%22%2C+%22nonce%22%3A+%22014f%3Abff9a07c%22%7D",
},
// https://tools.ietf.org/html/rfc6749#section-4.2.2
// Fragment should be encoded as application/x-www-form-urlencoded (%-escaped, spaces are represented as '+')
"fragment": {
URL: "https://foo.com/path?abc=123",
Output: ResponseData{"access_token": "12345", "state": state},
RedirectInFragment: true,
ExpectedURL: "https://foo.com/path?abc=123#access_token=12345&state=%7B%22then%22%3A+%22%2Findex.html%3Fa%3D1%26b%3D%252B%23fragment%22%2C+%22nonce%22%3A+%22014f%3Abff9a07c%22%7D",
},
}
for k, tc := range testcases {
resp := &Response{
Type: REDIRECT,
URL: tc.URL,
Output: tc.Output,
RedirectInFragment: tc.RedirectInFragment,
}
result, err := resp.GetRedirectUrl()
if err != nil {
t.Errorf("%s: %v", k, err)
continue
}
if result != tc.ExpectedURL {
t.Errorf("%s: expected\n\t%v, got\n\t%v", k, tc.ExpectedURL, result)
continue
}
var params url.Values
if tc.RedirectInFragment {
params, err = url.ParseQuery(strings.SplitN(result, "#", 2)[1])
if err != nil {
t.Errorf("%s: %v", k, err)
continue
}
} else {
parsedResult, err := url.Parse(result)
if err != nil {
t.Errorf("%s: %v", k, err)
continue
}
params = parsedResult.Query()
}
if params["state"][0] != state {
t.Errorf("%s: expected\n\t%v, got\n\t%v", k, state, params["state"][0])
continue
}
}
}

32
vendor/github.com/RangelReale/osin/server.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package osin
import (
"time"
)
// Server is an OAuth2 implementation
type Server struct {
Config *ServerConfig
Storage Storage
AuthorizeTokenGen AuthorizeTokenGen
AccessTokenGen AccessTokenGen
Now func() time.Time
}
// NewServer creates a new server instance
func NewServer(config *ServerConfig, storage Storage) *Server {
return &Server{
Config: config,
Storage: storage,
AuthorizeTokenGen: &AuthorizeTokenGenDefault{},
AccessTokenGen: &AccessTokenGenDefault{},
Now: time.Now,
}
}
// NewResponse creates a new response for the server
func (s *Server) NewResponse() *Response {
r := NewResponse(s.Storage)
r.ErrorStatusCode = s.Config.ErrorStatusCode
return r
}

59
vendor/github.com/RangelReale/osin/storage.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
package osin
import (
"errors"
)
var (
// ErrNotFound is the error returned by Storage Get<...> and Load<...> functions in case
// no entity is found in the storage. E.g. Storage.GetClient() returns ErrNotFound when
// client is not found. All other returned errors must be treated as storage-specific errors,
// like "connection lost", "connection refused", etc.
ErrNotFound = errors.New("Entity not found")
)
// Storage interface
type Storage interface {
// Clone the storage if needed. For example, using mgo, you can clone the session with session.Clone
// to avoid concurrent access problems.
// This is to avoid cloning the connection at each method access.
// Can return itself if not a problem.
Clone() Storage
// Close the resources the Storage potentially holds (using Clone for example)
Close()
// GetClient loads the client by id (client_id)
GetClient(id string) (Client, error)
// SaveAuthorize saves authorize data.
SaveAuthorize(*AuthorizeData) error
// LoadAuthorize looks up AuthorizeData by a code.
// Client information MUST be loaded together.
// Optionally can return error if expired.
LoadAuthorize(code string) (*AuthorizeData, error)
// RemoveAuthorize revokes or deletes the authorization code.
RemoveAuthorize(code string) error
// SaveAccess writes AccessData.
// If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh.
SaveAccess(*AccessData) error
// LoadAccess retrieves access data by token. Client information MUST be loaded together.
// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
// Optionally can return error if expired.
LoadAccess(token string) (*AccessData, error)
// RemoveAccess revokes or deletes an AccessData.
RemoveAccess(token string) error
// LoadRefresh retrieves refresh AccessData. Client information MUST be loaded together.
// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
// Optionally can return error if expired.
LoadRefresh(token string) (*AccessData, error)
// RemoveRefresh revokes or deletes refresh AccessData.
RemoveRefresh(token string) error
}

158
vendor/github.com/RangelReale/osin/storage_test.go generated vendored Normal file
View File

@@ -0,0 +1,158 @@
package osin
import (
"strconv"
"time"
)
type TestingStorage struct {
clients map[string]Client
authorize map[string]*AuthorizeData
access map[string]*AccessData
refresh map[string]string
}
func NewTestingStorage() *TestingStorage {
r := &TestingStorage{
clients: make(map[string]Client),
authorize: make(map[string]*AuthorizeData),
access: make(map[string]*AccessData),
refresh: make(map[string]string),
}
r.clients["1234"] = &DefaultClient{
Id: "1234",
Secret: "aabbccdd",
RedirectUri: "http://localhost:14000/appauth",
}
r.clients["public-client"] = &DefaultClient{
Id: "public-client",
RedirectUri: "http://localhost:14000/appauth",
}
r.authorize["9999"] = &AuthorizeData{
Client: r.clients["1234"],
Code: "9999",
ExpiresIn: 3600,
CreatedAt: time.Now(),
RedirectUri: "http://localhost:14000/appauth",
}
r.access["9999"] = &AccessData{
Client: r.clients["1234"],
AuthorizeData: r.authorize["9999"],
AccessToken: "9999",
ExpiresIn: 3600,
CreatedAt: time.Now(),
}
r.access["r9999"] = &AccessData{
Client: r.clients["1234"],
AuthorizeData: r.authorize["9999"],
AccessData: r.access["9999"],
AccessToken: "9999",
RefreshToken: "r9999",
ExpiresIn: 3600,
CreatedAt: time.Now(),
}
r.refresh["r9999"] = "9999"
return r
}
func (s *TestingStorage) Clone() Storage {
return s
}
func (s *TestingStorage) Close() {
}
func (s *TestingStorage) GetClient(id string) (Client, error) {
if c, ok := s.clients[id]; ok {
return c, nil
}
return nil, ErrNotFound
}
func (s *TestingStorage) SetClient(id string, client Client) error {
s.clients[id] = client
return nil
}
func (s *TestingStorage) SaveAuthorize(data *AuthorizeData) error {
s.authorize[data.Code] = data
return nil
}
func (s *TestingStorage) LoadAuthorize(code string) (*AuthorizeData, error) {
if d, ok := s.authorize[code]; ok {
return d, nil
}
return nil, ErrNotFound
}
func (s *TestingStorage) RemoveAuthorize(code string) error {
delete(s.authorize, code)
return nil
}
func (s *TestingStorage) SaveAccess(data *AccessData) error {
s.access[data.AccessToken] = data
if data.RefreshToken != "" {
s.refresh[data.RefreshToken] = data.AccessToken
}
return nil
}
func (s *TestingStorage) LoadAccess(code string) (*AccessData, error) {
if d, ok := s.access[code]; ok {
return d, nil
}
return nil, ErrNotFound
}
func (s *TestingStorage) RemoveAccess(code string) error {
delete(s.access, code)
return nil
}
func (s *TestingStorage) LoadRefresh(code string) (*AccessData, error) {
if d, ok := s.refresh[code]; ok {
return s.LoadAccess(d)
}
return nil, ErrNotFound
}
func (s *TestingStorage) RemoveRefresh(code string) error {
delete(s.refresh, code)
return nil
}
// Predictable testing token generation
type TestingAuthorizeTokenGen struct {
counter int64
}
func (a *TestingAuthorizeTokenGen) GenerateAuthorizeToken(data *AuthorizeData) (ret string, err error) {
a.counter++
return strconv.FormatInt(a.counter, 10), nil
}
type TestingAccessTokenGen struct {
acounter int64
rcounter int64
}
func (a *TestingAccessTokenGen) GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) {
a.acounter++
accesstoken = strconv.FormatInt(a.acounter, 10)
if generaterefresh {
a.rcounter++
refreshtoken = "r" + strconv.FormatInt(a.rcounter, 10)
}
return
}

33
vendor/github.com/RangelReale/osin/tokengen.go generated vendored Normal file
View File

@@ -0,0 +1,33 @@
package osin
import (
"encoding/base64"
"github.com/pborman/uuid"
)
// AuthorizeTokenGenDefault is the default authorization token generator
type AuthorizeTokenGenDefault struct {
}
// GenerateAuthorizeToken generates a base64-encoded UUID code
func (a *AuthorizeTokenGenDefault) GenerateAuthorizeToken(data *AuthorizeData) (ret string, err error) {
token := uuid.NewRandom()
return base64.RawURLEncoding.EncodeToString([]byte(token)), nil
}
// AccessTokenGenDefault is the default authorization token generator
type AccessTokenGenDefault struct {
}
// GenerateAccessToken generates base64-encoded UUID access and refresh tokens
func (a *AccessTokenGenDefault) GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) {
token := uuid.NewRandom()
accesstoken = base64.RawURLEncoding.EncodeToString([]byte(token))
if generaterefresh {
rtoken := uuid.NewRandom()
refreshtoken = base64.RawURLEncoding.EncodeToString([]byte(rtoken))
}
return
}

114
vendor/github.com/RangelReale/osin/urivalidate.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
package osin
import (
"errors"
"fmt"
"net/url"
"strings"
)
// error returned when validation don't match
type UriValidationError string
func (e UriValidationError) Error() string {
return string(e)
}
func newUriValidationError(msg string, base string, redirect string) UriValidationError {
return UriValidationError(fmt.Sprintf("%s: %s / %s", msg, base, redirect))
}
// ValidateUriList validates that redirectUri is contained in baseUriList.
// baseUriList may be a string separated by separator.
// If separator is blank, validate only 1 URI.
func ValidateUriList(baseUriList string, redirectUri string, separator string) error {
// make a list of uris
var slist []string
if separator != "" {
slist = strings.Split(baseUriList, separator)
} else {
slist = make([]string, 0)
slist = append(slist, baseUriList)
}
for _, sitem := range slist {
err := ValidateUri(sitem, redirectUri)
// validated, return no error
if err == nil {
return nil
}
// if there was an error that is not a validation error, return it
if _, iok := err.(UriValidationError); !iok {
return err
}
}
return newUriValidationError("urls don't validate", baseUriList, redirectUri)
}
// ValidateUri validates that redirectUri is contained in baseUri
func ValidateUri(baseUri string, redirectUri string) error {
if baseUri == "" || redirectUri == "" {
return errors.New("urls cannot be blank.")
}
// parse base url
base, err := url.Parse(baseUri)
if err != nil {
return err
}
// parse passed url
redirect, err := url.Parse(redirectUri)
if err != nil {
return err
}
// must not have fragment
if base.Fragment != "" || redirect.Fragment != "" {
return errors.New("url must not include fragment.")
}
// check if urls match
if base.Scheme != redirect.Scheme {
return newUriValidationError("scheme mismatch", baseUri, redirectUri)
}
if base.Host != redirect.Host {
return newUriValidationError("host mismatch", baseUri, redirectUri)
}
// allow exact path matches
if base.Path == redirect.Path {
return nil
}
// ensure prefix matches are actually subpaths
requiredPrefix := strings.TrimRight(base.Path, "/") + "/"
if !strings.HasPrefix(redirect.Path, requiredPrefix) {
return newUriValidationError("path is not a subpath", baseUri, redirectUri)
}
// ensure prefix matches don't contain path traversals
for _, s := range strings.Split(strings.TrimPrefix(redirect.Path, requiredPrefix), "/") {
if s == ".." {
return newUriValidationError("subpath cannot contain path traversal", baseUri, redirectUri)
}
}
return nil
}
// Returns the first uri from an uri list
func FirstUri(baseUriList string, separator string) string {
if separator != "" {
slist := strings.Split(baseUriList, separator)
if len(slist) > 0 {
return slist[0]
}
} else {
return baseUriList
}
return ""
}

120
vendor/github.com/RangelReale/osin/urivalidate_test.go generated vendored Normal file
View File

@@ -0,0 +1,120 @@
package osin
import (
"testing"
)
func TestURIValidate(t *testing.T) {
valid := [][]string{
{
// Exact match
"http://localhost:14000/appauth",
"http://localhost:14000/appauth",
},
{
// Trailing slash
"http://www.google.com/myapp",
"http://www.google.com/myapp/",
},
{
// Exact match with trailing slash
"http://www.google.com/myapp/",
"http://www.google.com/myapp/",
},
{
// Subpath
"http://www.google.com/myapp",
"http://www.google.com/myapp/interface/implementation",
},
{
// Subpath with trailing slash
"http://www.google.com/myapp/",
"http://www.google.com/myapp/interface/implementation",
},
{
// Subpath with things that are close to path traversals, but aren't
"http://www.google.com/myapp",
"http://www.google.com/myapp/.../..implementation../...",
},
{
// If the allowed basepath contains path traversals, allow them?
"http://www.google.com/traversal/../allowed",
"http://www.google.com/traversal/../allowed/with/subpath",
},
}
for _, v := range valid {
if err := ValidateUri(v[0], v[1]); err != nil {
t.Errorf("Expected ValidateUri(%s, %s) to succeed, got %v", v[0], v[1], err)
}
}
invalid := [][]string{
{
// Doesn't satisfy base path
"http://localhost:14000/appauth",
"http://localhost:14000/app",
},
{
// Doesn't satisfy base path
"http://localhost:14000/app/",
"http://localhost:14000/app",
},
{
// Not a subpath of base path
"http://localhost:14000/appauth",
"http://localhost:14000/appauthmodifiedpath",
},
{
// Host mismatch
"http://www.google.com/myapp",
"http://www2.google.com/myapp",
},
{
// Scheme mismatch
"http://www.google.com/myapp",
"https://www.google.com/myapp",
},
{
// Path traversal
"http://www.google.com/myapp",
"http://www.google.com/myapp/..",
},
{
// Embedded path traversal
"http://www.google.com/myapp",
"http://www.google.com/myapp/../test",
},
{
// Not a subpath
"http://www.google.com/myapp",
"http://www.google.com/myapp../test",
},
}
for _, v := range invalid {
if err := ValidateUri(v[0], v[1]); err == nil {
t.Errorf("Expected ValidateUri(%s, %s) to fail", v[0], v[1])
}
}
}
func TestURIListValidate(t *testing.T) {
// V1
if err := ValidateUriList("http://localhost:14000/appauth", "http://localhost:14000/appauth", ""); err != nil {
t.Errorf("V1: %s", err)
}
// V2
if err := ValidateUriList("http://localhost:14000/appauth", "http://localhost:14000/app", ""); err == nil {
t.Error("V2 should have failed")
}
// V3
if err := ValidateUriList("http://xxx:14000/appauth;http://localhost:14000/appauth", "http://localhost:14000/appauth", ";"); err != nil {
t.Errorf("V3: %s", err)
}
// V4
if err := ValidateUriList("http://xxx:14000/appauth;http://localhost:14000/appauth", "http://localhost:14000/app", ";"); err == nil {
t.Error("V4 should have failed")
}
}

108
vendor/github.com/RangelReale/osin/util.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
package osin
import (
"encoding/base64"
"errors"
"net/http"
"strings"
)
// Parse basic authentication header
type BasicAuth struct {
Username string
Password string
}
// Parse bearer authentication header
type BearerAuth struct {
Code string
}
// CheckClientSecret determines whether the given secret matches a secret held by the client.
// Public clients return true for a secret of ""
func CheckClientSecret(client Client, secret string) bool {
switch client := client.(type) {
case ClientSecretMatcher:
// Prefer the more secure method of giving the secret to the client for comparison
return client.ClientSecretMatches(secret)
default:
// Fallback to the less secure method of extracting the plain text secret from the client for comparison
return client.GetSecret() == secret
}
}
// Return authorization header data
func CheckBasicAuth(r *http.Request) (*BasicAuth, error) {
if r.Header.Get("Authorization") == "" {
return nil, nil
}
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(s) != 2 || s[0] != "Basic" {
return nil, errors.New("Invalid authorization header")
}
b, err := base64.StdEncoding.DecodeString(s[1])
if err != nil {
return nil, err
}
pair := strings.SplitN(string(b), ":", 2)
if len(pair) != 2 {
return nil, errors.New("Invalid authorization message")
}
return &BasicAuth{Username: pair[0], Password: pair[1]}, nil
}
// Return "Bearer" token from request. The header has precedence over query string.
func CheckBearerAuth(r *http.Request) *BearerAuth {
authHeader := r.Header.Get("Authorization")
authForm := r.Form.Get("code")
if authHeader == "" && authForm == "" {
return nil
}
token := authForm
if authHeader != "" {
s := strings.SplitN(authHeader, " ", 2)
if (len(s) != 2 || strings.ToLower(s[0]) != "bearer") && token == "" {
return nil
}
//Use authorization header token only if token type is bearer else query string access token would be returned
if len(s) > 0 && strings.ToLower(s[0]) == "bearer" {
token = s[1]
}
}
return &BearerAuth{Code: token}
}
// getClientAuth checks client basic authentication in params if allowed,
// otherwise gets it from the header.
// Sets an error on the response if no auth is present or a server error occurs.
func getClientAuth(w *Response, r *http.Request, allowQueryParams bool) *BasicAuth {
if allowQueryParams {
// Allow for auth without password
if _, hasSecret := r.Form["client_secret"]; hasSecret {
auth := &BasicAuth{
Username: r.Form.Get("client_id"),
Password: r.Form.Get("client_secret"),
}
if auth.Username != "" {
return auth
}
}
}
auth, err := CheckBasicAuth(r)
if err != nil {
w.SetError(E_INVALID_REQUEST, "")
w.InternalError = err
return nil
}
if auth == nil {
w.SetError(E_INVALID_REQUEST, "")
w.InternalError = errors.New("Client authentication not sent")
return nil
}
return auth
}

136
vendor/github.com/RangelReale/osin/util_test.go generated vendored Normal file
View File

@@ -0,0 +1,136 @@
package osin
import (
"net/http"
"net/url"
"testing"
)
const (
badAuthValue = "Digest XHHHHHHH"
goodAuthValue = "Basic dGVzdDp0ZXN0"
goodBearerAuthValue = "Bearer BGFVTDUJDp0ZXN0"
)
func TestBasicAuth(t *testing.T) {
r := &http.Request{Header: make(http.Header)}
// Without any header
if b, err := CheckBasicAuth(r); b != nil || err != nil {
t.Errorf("Validated basic auth without header")
}
// with invalid header
r.Header.Set("Authorization", badAuthValue)
b, err := CheckBasicAuth(r)
if b != nil || err == nil {
t.Errorf("Validated invalid auth")
return
}
// with valid header
r.Header.Set("Authorization", goodAuthValue)
b, err = CheckBasicAuth(r)
if b == nil || err != nil {
t.Errorf("Could not extract basic auth")
return
}
// check extracted auth data
if b.Username != "test" || b.Password != "test" {
t.Errorf("Error decoding basic auth")
}
}
func TestGetClientAuth(t *testing.T) {
urlWithSecret, _ := url.Parse("http://host.tld/path?client_id=xxx&client_secret=yyy")
urlWithEmptySecret, _ := url.Parse("http://host.tld/path?client_id=xxx&client_secret=")
urlNoSecret, _ := url.Parse("http://host.tld/path?client_id=xxx")
headerNoAuth := make(http.Header)
headerBadAuth := make(http.Header)
headerBadAuth.Set("Authorization", badAuthValue)
headerOKAuth := make(http.Header)
headerOKAuth.Set("Authorization", goodAuthValue)
var tests = []struct {
header http.Header
url *url.URL
allowQueryParams bool
expectAuth bool
}{
{headerNoAuth, urlWithSecret, true, true},
{headerNoAuth, urlWithSecret, false, false},
{headerNoAuth, urlWithEmptySecret, true, true},
{headerNoAuth, urlWithEmptySecret, false, false},
{headerNoAuth, urlNoSecret, true, false},
{headerNoAuth, urlNoSecret, false, false},
{headerBadAuth, urlWithSecret, true, true},
{headerBadAuth, urlWithSecret, false, false},
{headerBadAuth, urlWithEmptySecret, true, true},
{headerBadAuth, urlWithEmptySecret, false, false},
{headerBadAuth, urlNoSecret, true, false},
{headerBadAuth, urlNoSecret, false, false},
{headerOKAuth, urlWithSecret, true, true},
{headerOKAuth, urlWithSecret, false, true},
{headerOKAuth, urlWithEmptySecret, true, true},
{headerOKAuth, urlWithEmptySecret, false, true},
{headerOKAuth, urlNoSecret, true, true},
{headerOKAuth, urlNoSecret, false, true},
}
for _, tt := range tests {
w := new(Response)
r := &http.Request{Header: tt.header, URL: tt.url}
r.ParseForm()
auth := getClientAuth(w, r, tt.allowQueryParams)
if tt.expectAuth && auth == nil {
t.Errorf("Auth should not be nil for %v", tt)
} else if !tt.expectAuth && auth != nil {
t.Errorf("Auth should be nil for %v", tt)
}
}
}
func TestBearerAuth(t *testing.T) {
r := &http.Request{Header: make(http.Header)}
// Without any header
if b := CheckBearerAuth(r); b != nil {
t.Errorf("Validated bearer auth without header")
}
// with invalid header
r.Header.Set("Authorization", badAuthValue)
b := CheckBearerAuth(r)
if b != nil {
t.Errorf("Validated invalid auth")
return
}
// with valid header
r.Header.Set("Authorization", goodBearerAuthValue)
b = CheckBearerAuth(r)
if b == nil {
t.Errorf("Could not extract bearer auth")
return
}
// check extracted auth data
if b.Code != "BGFVTDUJDp0ZXN0" {
t.Errorf("Error decoding bearer auth")
}
// extracts bearer auth from query string
url, _ := url.Parse("http://host.tld/path?code=XYZ")
r = &http.Request{URL: url}
r.ParseForm()
b = CheckBearerAuth(r)
if b.Code != "XYZ" {
t.Errorf("Error decoding bearer auth")
}
}

50
vendor/github.com/ansel1/merry/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,50 @@
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
vendor/

9
vendor/github.com/ansel1/merry/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,9 @@
language: go
go:
- 1.5
- 1.6
- 1.7
- 1.8
- 1.9
- tip

27
vendor/github.com/ansel1/merry/Gopkg.lock generated vendored Normal file
View File

@@ -0,0 +1,27 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert"]
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "b750880cdc8ce044e6f9bf3b331d8a392471c328107b8c3d42e3e11022d76858"
solver-name = "gps-cdcl"
solver-version = 1

26
vendor/github.com/ansel1/merry/Gopkg.toml generated vendored Normal file
View File

@@ -0,0 +1,26 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.1.4"

21
vendor/github.com/ansel1/merry/LICENSE.MIT generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Russ Egan
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.

193
vendor/github.com/ansel1/merry/README.md generated vendored Normal file
View File

@@ -0,0 +1,193 @@
merry [![Build Status](https://travis-ci.org/ansel1/merry.svg?branch=master)](https://travis-ci.org/ansel1/merry) [![GoDoc](https://godoc.org/github.com/ansel1/merry?status.png)](https://godoc.org/github.com/ansel1/merry) [![Go Report Card](https://goreportcard.com/badge/github.com/ansel1/merry)](https://goreportcard.com/report/github.com/ansel1/merry)
=====
Make your golang errors merry, with stacktraces, inheritance, and arbitrary additional context.
The package is largely based on http://github.com/go-errors/errors, with additional
inspiration from https://github.com/go-errgo/errgo and https://github.com/amattn/deeperror.
Installation
------------
go get github.com/ansel1/merry
Features
--------
Merry errors work a lot like google's golang.org/x/net/context package.
Merry errors wrap normal errors with a context of key/value pairs.
Like contexts, merry errors are immutable: adding a key/value to an error
always creates a new error which wraps the original.
`merry` comes with built-in support for adding information to errors:
* stacktraces
* overriding the error message
* HTTP status codes
* End user error messages
You can also add your own additional information.
The stack capturing feature can be turned off for better performance, though it's pretty fast. Benchmarks
on an early 2011 MacBook Pro, with go 1.7rc1:
BenchmarkNew_withStackCapture-8 1000000 1413 ns/op
BenchmarkNew_withoutStackCapture-8 10000000 218 ns/op
Details
-------
* New errors have a stacktrace captured where they are created
* Add a stacktrace to existing errors (captured where they are wrapped)
```go
err := lib.Read()
return merry.Wrap(err) // no-op if err is already merry
```
* Allow golang idiom of comparing an err value to an exported value, using `Is()`
```go
var ParseError = merry.New("Parse error")
func Parse() error {
err := ParseError.Here() // captures a stacktrace here
merry.Is(err, ParseError) // instead of err == ParseError
}
```
* Change the message on an error, while still using `Is()` to compare to the original error
```go
err := merry.WithMessage(ParseError, "Bad input")
merry.Is(err, ParseError) // yes it is
```
* `Is()` supports hierarchies of errors
```go
var ParseError = merry.New("Parse error")
var InvalidCharSet = merry.WithMessage(ParseError, "Invalid char set")
var InvalidSyntax = merry.WithMessage(ParseError, "Invalid syntax")
func Parse(s string) error {
// use chainable methods to add context
return InvalidCharSet.Here().WithMessagef("Invalid char set: %s", "UTF-8")
// or functions
// return merry.WithMessagef(merry.Here(InvalidCharSet), "Invalid char set: %s", "UTF-8")
}
func Check() {
err := Parse("fields")
merry.Is(err, ParseError) // yup
merry.Is(err, InvalidCharSet) // yup
merry.Is(err, InvalidSyntax) // nope
}
```
* Add an HTTP status code
```go
merry.HTTPCode(errors.New("regular error")) // 500
merry.HTTPCode(merry.New("merry error").WithHTTPCode(404)) // 404
```
* Set an alternate error message for end users
```go
e := merry.New("crash").WithUserMessage("nothing to see here")
merry.UserMessage(e) // returns "nothing to see here"
```
* Functions for printing error details
```go
err := merry.New("boom")
m := merry.Stacktrace(err) // just the stacktrace
m = merry.Details(err) // error message and stacktrace
fmt.Sprintf("%+v", err) == merry.Details(err) // errors implement fmt.Formatter
```
* Add your own context info
```go
err := merry.New("boom").WithValue("explosive", "black powder")
```
Basic Usage
-----------
The package contains functions for creating new errors with stacks, or adding a stack to `error`
instances. Functions with add context (e.g. `WithValue()`) work on any `error`, and will
automatically convert them to merry errors (with a stack) if necessary.
Capturing the stack can be globally disabled with `SetStackCaptureEnabled(false)`
Functions which get context values from errors also accept `error`, and will return default
values if the error is not merry, or doesn't have that key attached.
All the functions which create or attach context return concrete instances of `*Error`. `*Error`
implements methods to add context to the error (they mirror the functions and do
the same thing). They allow for a chainable syntax for adding context.
Example:
```go
package main
import (
"github.com/ansel1/merry"
"errors"
)
var InvalidInputs = errors.New("Input is invalid")
func main() {
// create a new error, with a stacktrace attached
err := merry.New("bad stuff happened")
// create a new error with format string, like fmt.Errorf
err = merry.Errorf("bad input: %v", os.Args)
// capture a fresh stacktrace from this callsite
err = merry.Here(InvalidInputs)
// Make err merry if it wasn't already. The stacktrace will be captured here if the
// error didn't already have one. Also useful to cast to *Error
err = merry.Wrap(err, 0)
// override the original error's message
err.WithMessagef("Input is invalid: %v", os.Args)
// Use Is to compare errors against values, which is a common golang idiom
merry.Is(err, InvalidInputs) // will be true
// associated an http code
err.WithHTTPCode(400)
perr := parser.Parse("blah")
err = Wrap(perr, 0)
// Get the original error back
merry.Unwrap(err) == perr // will be true
// Print the error to a string, with the stacktrace, if it has one
s := merry.Details(err)
// Just print the stacktrace (empty string if err is not a RichError)
s := merry.Stacktrace(err)
// Get the location of the error (the first line in the stacktrace)
file, line := merry.Location(err)
// Get an HTTP status code for an error. Defaults to 500 for non-nil errors, and 200 if err is nil.
code := merry.HTTPCode(err)
}
```
See inline docs for more details.
License
-------
This package is licensed under the MIT license, see LICENSE.MIT for details.

556
vendor/github.com/ansel1/merry/errors.go generated vendored Normal file
View File

@@ -0,0 +1,556 @@
package merry
// The merry package augments standard golang errors with stacktraces
// and other context information.
//
// You can add any context information to an error with `e = merry.WithValue(e, "code", 12345)`
// You can retrieve that value with `v, _ := merry.Value(e, "code").(int)`
//
// Any error augmented like this will automatically get a stacktrace attached, if it doesn't have one
// already. If you just want to add the stacktrace, use `Wrap(e)`
//
// It also providers a way to override an error's message:
//
// var InvalidInputs = errors.New("Bad inputs")
//
// `Here()` captures a new stacktrace, and WithMessagef() sets a new error message:
//
// return merry.Here(InvalidInputs).WithMessagef("Bad inputs: %v", inputs)
//
// Errors are immutable. All functions and methods which add context return new errors.
// But errors can still be compared to the originals with `Is()`
//
// if merry.Is(err, InvalidInputs) {
//
// Functions which add context to errors have equivalent methods on *Error, to allow
// convenient chaining:
//
// return merry.New("Invalid body").WithHTTPCode(400)
//
// merry.Errors also implement fmt.Formatter, similar to github.com/pkg/errors.
//
// fmt.Sprintf("%+v", e) == merry.Details(e)
//
// pkg/errors Cause() interface is not implemented (yet).
import (
"errors"
"fmt"
"io"
"runtime"
)
// MaxStackDepth is the maximum number of stackframes on any error.
var MaxStackDepth = 50
var captureStacks = true
var verbose = false
// StackCaptureEnabled returns whether stack capturing is enabled
func StackCaptureEnabled() bool {
return captureStacks
}
// SetStackCaptureEnabled sets stack capturing globally. Disabling stack capture can increase performance
func SetStackCaptureEnabled(enabled bool) {
captureStacks = enabled
}
// VerboseDefault returns the global default for verbose mode.
// When true, e.Error() == Details(e)
// When false, e.Error() == Message(e)
func VerboseDefault() bool {
return verbose
}
// SetVerboseDefault sets the global default for verbose mode.
// When true, e.Error() == Details(e)
// When false, e.Error() == Message(e)
func SetVerboseDefault(b bool) {
verbose = b
}
// Error extends the standard golang `error` interface with functions
// for attachment additional data to the error
type Error interface {
error
Appendf(format string, args ...interface{}) Error
Append(msg string) Error
Prepend(msg string) Error
Prependf(format string, args ...interface{}) Error
WithMessage(msg string) Error
WithMessagef(format string, args ...interface{}) Error
WithUserMessage(msg string) Error
WithUserMessagef(format string, args ...interface{}) Error
WithValue(key, value interface{}) Error
Here() Error
WithStackSkipping(skip int) Error
WithHTTPCode(code int) Error
fmt.Formatter
}
// New creates a new error, with a stack attached. The equivalent of golang's errors.New()
func New(msg string) Error {
return WrapSkipping(errors.New(msg), 1)
}
// Errorf creates a new error with a formatted message and a stack. The equivalent of golang's fmt.Errorf()
func Errorf(format string, a ...interface{}) Error {
return WrapSkipping(fmt.Errorf(format, a...), 1)
}
// UserError creates a new error with a message intended for display to an
// end user.
func UserError(msg string) Error {
return WrapSkipping(errors.New(""), 1).WithUserMessage(msg)
}
// UserErrorf is like UserError, but uses fmt.Sprintf()
func UserErrorf(format string, a ...interface{}) Error {
return WrapSkipping(errors.New(""), 1).WithUserMessagef(format, a...)
}
// Wrap turns the argument into a merry.Error. If the argument already is a
// merry.Error, this is a no-op.
// If e == nil, return nil
func Wrap(e error) Error {
return WrapSkipping(e, 1)
}
// WrapSkipping turns the error arg into a merry.Error if the arg is not
// already a merry.Error.
// If e is nil, return nil.
// If a merry.Error is created by this call, the stack captured will skip
// `skip` frames (0 is the call site of `WrapSkipping()`)
func WrapSkipping(e error, skip int) Error {
switch e1 := e.(type) {
case nil:
return nil
case *merryErr:
return e1
default:
return &merryErr{
err: e,
key: stack,
value: captureStack(skip + 1),
}
}
}
// WithValue adds a context an error. If the key was already set on e,
// the new value will take precedence.
// If e is nil, returns nil.
func WithValue(e error, key, value interface{}) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).WithValue(key, value)
}
// Value returns the value for key, or nil if not set.
// If e is nil, returns nil.
func Value(e error, key interface{}) interface{} {
for {
switch m := e.(type) {
case nil:
return nil
case *merryErr:
if m.key == key {
return m.value
}
e = m.err
default:
return nil
}
}
}
// Values returns a map of all values attached to the error
// If a key has been attached multiple times, the map will
// contain the last value mapped
// If e is nil, returns nil.
func Values(e error) map[interface{}]interface{} {
if e == nil {
return nil
}
var values map[interface{}]interface{}
for {
w, ok := e.(*merryErr)
if !ok {
return values
}
if values == nil {
values = make(map[interface{}]interface{}, 1)
}
if _, ok := values[w.key]; !ok {
values[w.key] = w.value
}
e = w.err
}
}
// Here returns an error with a new stacktrace, at the call site of Here().
// Useful when returning copies of exported package errors.
// If e is nil, returns nil.
func Here(e error) Error {
switch m := e.(type) {
case nil:
return nil
case *merryErr:
// optimization: only capture the stack once, since its expensive
return m.WithStackSkipping(1)
default:
return WrapSkipping(e, 1)
}
}
// Stack returns the stack attached to an error, or nil if one is not attached
// If e is nil, returns nil.
func Stack(e error) []uintptr {
stack, _ := Value(e, stack).([]uintptr)
return stack
}
// WithHTTPCode returns an error with an http code attached.
// If e is nil, returns nil.
func WithHTTPCode(e error, code int) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).WithHTTPCode(code)
}
// HTTPCode converts an error to an http status code. All errors
// map to 500, unless the error has an http code attached.
// If e is nil, returns 200.
func HTTPCode(e error) int {
if e == nil {
return 200
}
code, _ := Value(e, httpCode).(int)
if code == 0 {
return 500
}
return code
}
// UserMessage returns the end-user safe message. Returns empty if not set.
// If e is nil, returns "".
func UserMessage(e error) string {
if e == nil {
return ""
}
msg, _ := Value(e, userMessage).(string)
return msg
}
// Message returns just the error message. It is equivalent to
// Error() when Verbose is false.
// The behavior of Error() is (pseudo-code):
//
// if verbose
// Details(e)
// else
// Message(e) || UserMessage(e)
//
// If e is nil, returns "".
func Message(e error) string {
if e == nil {
return ""
}
m, _ := Value(e, message).(string)
if m == "" {
return Unwrap(e).Error()
}
return m
}
// WithMessage returns an error with a new message.
// The resulting error's Error() method will return
// the new message.
// If e is nil, returns nil.
func WithMessage(e error, msg string) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).WithValue(message, msg)
}
// WithMessagef is the same as WithMessage(), using fmt.Sprintf().
func WithMessagef(e error, format string, a ...interface{}) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).WithMessagef(format, a...)
}
// WithUserMessage adds a message which is suitable for end users to see.
// If e is nil, returns nil.
func WithUserMessage(e error, msg string) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).WithUserMessage(msg)
}
// WithUserMessagef is the same as WithMessage(), using fmt.Sprintf()
func WithUserMessagef(e error, format string, args ...interface{}) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).WithUserMessagef(format, args...)
}
// Append a message after the current error message, in the format "original: new".
// If e == nil, return nil.
func Append(e error, msg string) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).Append(msg)
}
// Appendf is the same as Append, but uses fmt.Sprintf().
func Appendf(e error, format string, args ...interface{}) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).Appendf(format, args...)
}
// Prepend a message before the current error message, in the format "new: original".
// If e == nil, return nil.
func Prepend(e error, msg string) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).Prepend(msg)
}
// Prependf is the same as Prepend, but uses fmt.Sprintf()
func Prependf(e error, format string, args ...interface{}) Error {
if e == nil {
return nil
}
return WrapSkipping(e, 1).Prependf(format, args...)
}
// Is checks whether e is equal to or wraps the original, at any depth.
// If e == nil, return false.
// This is useful if your package uses the common golang pattern of
// exported error constants. If your package exports an ErrEOF constant,
// which is initialized like this:
//
// var ErrEOF = errors.New("End of file error")
//
// ...and your user wants to compare an error returned by your package
// with ErrEOF:
//
// err := urpack.Read()
// if err == urpack.ErrEOF {
//
// ...the comparison will fail if the error has been wrapped by merry
// at some point. Replace the comparison with:
//
// if merry.Is(err, urpack.ErrEOF) {
//
func Is(e error, originals ...error) bool {
is := func(e, original error) bool {
for {
if e == original {
return true
}
if e == nil || original == nil {
return false
}
w, ok := e.(*merryErr)
if !ok {
return false
}
e = w.err
}
}
for _, o := range originals {
if is(e, o) {
return true
}
}
return false
}
// Unwrap returns the innermost underlying error.
// Only useful in advanced cases, like if you need to
// cast the underlying error to some type to get
// additional information from it.
// If e == nil, return nil.
func Unwrap(e error) error {
if e == nil {
return nil
}
for {
w, ok := e.(*merryErr)
if !ok {
return e
}
e = w.err
}
}
func captureStack(skip int) []uintptr {
if !captureStacks {
return nil
}
stack := make([]uintptr, MaxStackDepth)
length := runtime.Callers(2+skip, stack[:])
return stack[:length]
}
type errorProperty string
const (
stack errorProperty = "stack"
message = "message"
httpCode = "http status code"
userMessage = "user message"
)
type merryErr struct {
err error
key, value interface{}
}
// Format implements fmt.Formatter
func (e *merryErr) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
io.WriteString(s, Details(e))
return
}
fallthrough
case 's':
io.WriteString(s, e.Error())
case 'q':
fmt.Fprintf(s, "%q", e.Error())
}
}
// make sure merryErr implements Error
var _ Error = (*merryErr)(nil)
// Error implements golang's error interface
// returns the message value if set, otherwise
// delegates to inner error
func (e *merryErr) Error() string {
if verbose {
return Details(e)
}
m := Message(e)
if m == "" {
return UserMessage(e)
}
return m
}
// return a new error with additional context
func (e *merryErr) WithValue(key, value interface{}) Error {
if e == nil {
return nil
}
return &merryErr{
err: e,
key: key,
value: value,
}
}
// Shorthand for capturing a new stack trace
func (e *merryErr) Here() Error {
if e == nil {
return nil
}
return e.WithStackSkipping(1)
}
// return a new error with a new stack capture
func (e *merryErr) WithStackSkipping(skip int) Error {
if e == nil {
return nil
}
return &merryErr{
err: e,
key: stack,
value: captureStack(skip + 1),
}
}
// return a new error with an http status code attached
func (e *merryErr) WithHTTPCode(code int) Error {
if e == nil {
return nil
}
return e.WithValue(httpCode, code)
}
// return a new error with a new message
func (e *merryErr) WithMessage(msg string) Error {
if e == nil {
return nil
}
return e.WithValue(message, msg)
}
// return a new error with a new formatted message
func (e *merryErr) WithMessagef(format string, a ...interface{}) Error {
if e == nil {
return nil
}
return e.WithMessage(fmt.Sprintf(format, a...))
}
// Add a message which is suitable for end users to see
func (e *merryErr) WithUserMessage(msg string) Error {
if e == nil {
return nil
}
return e.WithValue(userMessage, msg)
}
// Add a message which is suitable for end users to see
func (e *merryErr) WithUserMessagef(format string, args ...interface{}) Error {
if e == nil {
return nil
}
return e.WithUserMessage(fmt.Sprintf(format, args...))
}
// Append a message after the current error message, in the format "original: new"
func (e *merryErr) Append(msg string) Error {
if e == nil {
return nil
}
return e.WithMessagef("%s: %s", e.Error(), msg)
}
// Append a message after the current error message, in the format "original: new"
func (e *merryErr) Appendf(format string, args ...interface{}) Error {
if e == nil {
return nil
}
return e.Append(fmt.Sprintf(format, args...))
}
// Prepend a message before the current error message, in the format "new: original"
func (e *merryErr) Prepend(msg string) Error {
if e == nil {
return nil
}
return e.WithMessagef("%s: %s", msg, e.Error())
}
// Prepend a message before the current error message, in the format "new: original"
func (e *merryErr) Prependf(format string, args ...interface{}) Error {
if e == nil {
return nil
}
return e.Prepend(fmt.Sprintf(format, args...))
}

556
vendor/github.com/ansel1/merry/errors_test.go generated vendored Normal file
View File

@@ -0,0 +1,556 @@
package merry
import (
"errors"
"fmt"
"reflect"
"runtime"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNew(t *testing.T) {
_, _, rl, _ := runtime.Caller(0)
err := New("bang")
if HTTPCode(err) != 500 {
t.Errorf("http code should have been 500, was %v", HTTPCode(err))
}
if err.Error() != "bang" {
t.Errorf("error message should have been bang, was %v", err.Error())
}
f, l := Location(err)
if !strings.Contains(f, "errors_test.go") {
t.Errorf("error message should have contained errors_test.go, was %s", f)
}
if l != rl+1 {
t.Errorf("error line should have been %d, was %d", rl+1, 8)
}
}
func TestErrorf(t *testing.T) {
_, _, rl, _ := runtime.Caller(0)
err := Errorf("chitty chitty %v %v", "bang", "bang")
if HTTPCode(err) != 500 {
t.Errorf("http code should have been 500, was %v", HTTPCode(err))
}
if err.Error() != "chitty chitty bang bang" {
t.Errorf("error message should have been chitty chitty bang bang, was %v", err.Error())
}
f, l := Location(err)
if !strings.Contains(f, "errors_test.go") {
t.Errorf("error message should have contained errors_test.go, was %s", f)
}
if l != rl+1 {
t.Errorf("error line should have been %d, was %d", rl+1, 8)
}
}
func TestUserError(t *testing.T) {
_, _, rl, _ := runtime.Caller(0)
err := UserError("bang")
assert.Equal(t, "bang", UserMessage(err))
assert.Empty(t, Message(err))
_, l := Location(err)
assert.Equal(t, rl+1, l)
}
func TestUserErrorf(t *testing.T) {
_, _, rl, _ := runtime.Caller(0)
err := UserErrorf("bang %v", "bang")
assert.Equal(t, "bang bang", UserMessage(err))
assert.Empty(t, Message(err))
_, l := Location(err)
assert.Equal(t, rl+1, l)
}
func TestDetails(t *testing.T) {
var err error = New("bang")
deets := Details(err)
t.Log(deets)
lines := strings.Split(deets, "\n")
if lines[0] != "bang" {
t.Errorf("first line should have been bang: %v", lines[0])
}
if !strings.Contains(deets, Stacktrace(err)) {
t.Error("should have contained the error stacktrace")
}
err = WithUserMessage(err, "stay calm")
deets = Details(err)
t.Log(deets)
assert.Contains(t, deets, "User Message: stay calm")
// Allow nil error
assert.Empty(t, Details(nil))
}
func TestStacktrace(t *testing.T) {
_, _, rl, _ := runtime.Caller(0)
var err error = New("bang")
assert.NotEmpty(t, Stack(err))
s := Stacktrace(err)
t.Log(s)
lines := strings.Split(s, "\n")
assert.NotEmpty(t, lines)
assert.Equal(t, "github.com/ansel1/merry.TestStacktrace", lines[0])
assert.Contains(t, lines[1], fmt.Sprintf("errors_test.go:%d", rl+1))
// Allow nil error
assert.Empty(t, Stacktrace(nil))
}
func TestWrap(t *testing.T) {
err := errors.New("simple")
_, _, rl, _ := runtime.Caller(0)
wrapped := WrapSkipping(err, 0)
f, l := Location(wrapped)
if !strings.Contains(f, "errors_test.go") {
t.Errorf("error message should have contained errors_test.go, was %s", f)
}
if l != rl+1 {
t.Errorf("error line should have been %d, was %d", rl+1, l)
}
rich2 := WrapSkipping(wrapped, 0)
if wrapped != rich2 {
t.Error("rich and rich2 are not the same. Wrap should have been no-op if rich was already a RichError")
}
if !reflect.DeepEqual(Stack(wrapped), Stack(rich2)) {
t.Log(Details(rich2))
t.Error("wrap should have left the stacktrace alone if the original error already had a stack")
}
// wrapping nil -> nil
assert.Nil(t, Wrap(nil))
assert.Nil(t, WrapSkipping(nil, 1))
}
func TestHere(t *testing.T) {
ParseError := New("Parse error")
InvalidCharSet := WithMessage(ParseError, "Invalid charset").WithHTTPCode(400)
InvalidSyntax := ParseError.WithMessage("Syntax error")
if !Is(InvalidCharSet, ParseError) {
t.Error("InvalidCharSet should be a ParseError")
}
_, _, rl, _ := runtime.Caller(0)
pe := Here(ParseError)
_, l := Location(pe)
if l != rl+1 {
t.Errorf("Extend should capture a new stack. Expected %d, got %d", rl+1, l)
}
if !Is(pe, ParseError) {
t.Error("pe should be a ParseError")
}
if Is(pe, InvalidCharSet) {
t.Error("pe should not be an InvalidCharSet")
}
if pe.Error() != "Parse error" {
t.Errorf("child error's message is wrong, expected: Parse error, got %v", pe.Error())
}
icse := Here(InvalidCharSet)
if !Is(icse, ParseError) {
t.Error("icse should be a ParseError")
}
if !Is(icse, InvalidCharSet) {
t.Error("icse should be an InvalidCharSet")
}
if Is(icse, InvalidSyntax) {
t.Error("icse should not be an InvalidSyntax")
}
if icse.Error() != "Invalid charset" {
t.Errorf("child's message is wrong. Expected: Invalid charset, got: %v", icse.Error())
}
if HTTPCode(icse) != 400 {
t.Errorf("child's http code is wrong. Expected 400, got %v", HTTPCode(icse))
}
// nil -> nil
assert.Nil(t, Here(nil))
}
func TestUnwrap(t *testing.T) {
inner := errors.New("bing")
wrapper := WrapSkipping(inner, 0)
if Unwrap(wrapper) != inner {
t.Errorf("unwrapped error should have been the inner err, was %#v", inner)
}
doubleWrap := wrapper.WithMessage("blag")
if Unwrap(doubleWrap) != inner {
t.Errorf("unwrapped should recurse to inner, but got %#v", inner)
}
// nil -> nil
assert.Nil(t, Unwrap(nil))
}
func TestNilValues(t *testing.T) {
// Quirk of go
// http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#nil_in_nil_in_vals
// an interface value isn't nil unless both the type *and* the value are nil
// make sure we aren't accidentally returning nil values but non-nil types
type e struct{}
var anE *e
type f interface{}
var anF f
if anF != nil {
t.Error("anF should have been nil here, because it doesn't have a concete type yet")
}
anF = anE
if anF == nil {
t.Error("anF should have been not nil here, because it now has a concrete type")
}
if WithMessage(WithHTTPCode(Wrap(nil), 400), "hey") != nil {
t.Error("by using interfaces in all the returns, this should have remained a true nil value")
}
}
func TestIs(t *testing.T) {
ParseError := errors.New("blag")
cp := Here(ParseError)
if !Is(cp, ParseError) {
t.Error("Is(child, parent) should be true")
}
if Is(ParseError, cp) {
t.Error("Is(parent, child) should not be true")
}
if !Is(ParseError, ParseError) {
t.Error("errors are always themselves")
}
if !Is(cp, cp) {
t.Error("should work when comparing rich error to itself")
}
if Is(Here(ParseError), cp) {
t.Error("Is(sibling, sibling) should not be true")
}
err2 := errors.New("blag")
if Is(ParseError, err2) {
t.Error("These should not have been equal")
}
if Is(Here(err2), cp) {
t.Error("these were not copies of the same error")
}
if Is(Here(err2), ParseError) {
t.Error("underlying errors were not equal")
}
nilTests := []struct {
arg1, arg2 error
expect bool
msg string
}{
{nil, New("t"), false, "nil is not any concrete error"},
{New("t"), nil, false, "no concrete error is nil"},
{nil, nil, true, "nil is nil"},
}
for _, tst := range nilTests {
assert.Equal(t, tst.expect, Is(tst.arg1, tst.arg2), tst.msg)
}
}
func TestHTTPCode(t *testing.T) {
basicErr := errors.New("blag")
if c := HTTPCode(basicErr); c != 500 {
t.Errorf("default code should be 500, was %d", c)
}
err := New("blug")
if c := HTTPCode(err); c != 500 {
t.Errorf("default code for rich errors should be 500, was %d", c)
}
errWCode := err.WithHTTPCode(404)
if c := HTTPCode(errWCode); c != 404 {
t.Errorf("the code should be set to 404, was %d", c)
}
if HTTPCode(err) != 500 {
t.Error("original error should not have been modified")
}
// nil -> nil
assert.Nil(t, WithHTTPCode(nil, 404))
assert.Equal(t, 200, HTTPCode(nil), "The code for nil is 200 (ok)")
}
func TestImplicitWrapping(t *testing.T) {
// WithXXX functions will implicitly wrap non-merry errors
// but if they do so, they should skip a frame, so the merry error's stack
// appears to start wherever the WithXXX function was called
_, _, rl, _ := runtime.Caller(0)
tests := []struct {
f func() error
fname string
}{
{fname: "WithHTTPCode", f: func() error { return WithHTTPCode(errors.New("bug"), 404) }},
{fname: "WithUserMessage", f: func() error { return WithUserMessage(errors.New("bug"), "asdf") }},
{fname: "WithUserMessages", f: func() error { return WithUserMessagef(errors.New("bug"), "asdf") }},
{fname: "WithMessage", f: func() error { return WithMessage(errors.New("bug"), "asdf") }},
{fname: "WithMessagef", f: func() error { return WithMessagef(errors.New("bug"), "asdf") }},
{fname: "WithValue", f: func() error { return WithValue(errors.New("bug"), "asdf", "asdf") }},
{fname: "Append", f: func() error { return Append(errors.New("bug"), "asdf") }},
{fname: "Appendf", f: func() error { return Appendf(errors.New("bug"), "asdf") }},
{fname: "Prepend", f: func() error { return Prepend(errors.New("bug"), "asdf") }},
{fname: "Prependf", f: func() error { return Prependf(errors.New("bug"), "asdf") }},
}
for i, test := range tests {
t.Log("Testing ", test.fname)
err := test.f()
f, l := Location(err)
assert.Contains(t, f, "errors_test.go", "error message should have contained errors_test.go")
assert.Equal(t, rl+5+i, l, "error line number was incorrect")
}
}
func TestWithMessage(t *testing.T) {
err1 := New("blug")
err2 := err1.WithMessage("blee")
err3 := err2.WithMessage("red")
assert.EqualError(t, err1, "blug")
assert.EqualError(t, err2, "blee", "should have overridden the underlying message")
assert.EqualError(t, err3, "red")
assert.Equal(t, Stack(err1), Stack(err2), "stack should not have been altered")
// nil -> nil
assert.Nil(t, WithMessage(nil, ""))
}
func TestWithMessagef(t *testing.T) {
err1 := New("blug")
err2 := err1.WithMessagef("super %v", "stew")
err3 := err1.WithMessagef("blue %v", "red")
assert.EqualError(t, err1, "blug")
assert.EqualError(t, err2, "super stew")
assert.EqualError(t, err3, "blue red")
assert.Equal(t, Stack(err1), Stack(err2), "stack should not have been altered")
// nil -> nil
assert.Nil(t, WithMessagef(nil, "", ""))
}
func TestMessage(t *testing.T) {
tests := []error{
errors.New("one"),
WithMessage(errors.New("blue"), "one"),
New("one"),
}
for _, test := range tests {
assert.Equal(t, "one", test.Error())
assert.Equal(t, "one", Message(test))
}
// when verbose is on, Error() changes, but Message() doesn't
defer SetVerboseDefault(false)
SetVerboseDefault(true)
e := New("two")
assert.Equal(t, "two", Message(e))
assert.NotEqual(t, "two", e.Error())
// when error is nil, return ""
assert.Empty(t, Message(nil))
}
func TestWithUserMessage(t *testing.T) {
fault := New("seg fault")
e := WithUserMessage(fault, "a glitch")
assert.Equal(t, "seg fault", e.Error())
assert.Equal(t, "a glitch", UserMessage(e))
e = WithUserMessagef(e, "not a %s deal", "huge")
assert.Equal(t, "not a huge deal", UserMessage(e))
// If user message is set and regular message isn't, set regular message to user message
e = New("").WithUserMessage("a blag")
assert.Equal(t, "a blag", UserMessage(e))
assert.Equal(t, "a blag", e.Error())
}
func TestAppend(t *testing.T) {
blug := New("blug")
err := blug.Append("blog")
assert.Equal(t, err.Error(), "blug: blog")
err = Append(err, "blig")
assert.Equal(t, err.Error(), "blug: blog: blig")
err = blug.Appendf("%s", "blog")
assert.Equal(t, err.Error(), "blug: blog")
err = Appendf(err, "%s", "blig")
assert.Equal(t, err.Error(), "blug: blog: blig")
// nil -> nil
assert.Nil(t, Append(nil, ""))
assert.Nil(t, Appendf(nil, "", ""))
}
func TestPrepend(t *testing.T) {
blug := New("blug")
err := blug.Prepend("blog")
assert.Equal(t, err.Error(), "blog: blug")
err = Prepend(err, "blig")
assert.Equal(t, err.Error(), "blig: blog: blug")
err = blug.Prependf("%s", "blog")
assert.Equal(t, err.Error(), "blog: blug")
err = Prependf(err, "%s", "blig")
assert.Equal(t, err.Error(), "blig: blog: blug")
// nil -> nil
assert.Nil(t, Prepend(nil, ""))
assert.Nil(t, Prependf(nil, "", ""))
}
func TestLocation(t *testing.T) {
// nil -> nil
f, l := Location(nil)
assert.Equal(t, "", f)
assert.Equal(t, 0, l)
}
func TestSourceLine(t *testing.T) {
source := SourceLine(nil)
assert.Equal(t, source, "")
err := New("foo")
source = SourceLine(err)
t.Log(source)
assert.NotEqual(t, source, "")
parts := strings.Split(source, ":")
assert.Equal(t, len(parts), 2)
if !strings.HasSuffix(parts[0], "errors_test.go") {
t.Error("source line should contain file name")
}
if i, e := strconv.Atoi(parts[1]); e != nil {
t.Errorf("not a number: %s", parts[1])
} else if i <= 0 {
t.Errorf("source line must be > 1: %s", parts[1])
}
}
func TestValue(t *testing.T) {
// nil -> nil
assert.Nil(t, WithValue(nil, "", ""))
assert.Nil(t, Value(nil, ""))
}
func TestValues(t *testing.T) {
// nil -> nil
values := Values(nil)
assert.Nil(t, values)
var e error
e = New("bad stuff")
e = WithValue(e, "key1", "val1")
e = WithValue(e, "key2", "val2")
values = Values(e)
assert.NotNil(t, values)
assert.Equal(t, values["key1"], "val1")
assert.Equal(t, values["key2"], "val2")
assert.NotNil(t, values[stack])
// make sure the last value attached is returned
e = WithValue(e, "key3", "val3")
e = WithValue(e, "key3", "val4")
values = Values(e)
assert.Equal(t, values["key3"], "val4")
}
func TestStackCaptureEnabled(t *testing.T) {
// on by default
assert.True(t, StackCaptureEnabled())
SetStackCaptureEnabled(false)
assert.False(t, StackCaptureEnabled())
e := New("yikes")
assert.Empty(t, Stack(e))
// let's just make sure none of the print functions bomb when there's no stack
assert.Empty(t, SourceLine(e))
f, l := Location(e)
assert.Empty(t, f)
assert.Equal(t, 0, l)
assert.Empty(t, Stacktrace(e))
assert.NotPanics(t, func() { Details(e) })
// turn it back on
SetStackCaptureEnabled(true)
assert.True(t, StackCaptureEnabled())
e = New("mommy")
assert.NotEmpty(t, Stack(e))
}
func TestVerboseDefault(t *testing.T) {
defer SetVerboseDefault(false)
// off by default
assert.False(t, VerboseDefault())
SetVerboseDefault(true)
assert.True(t, VerboseDefault())
e := New("yikes")
// test verbose on
assert.Equal(t, Details(e), e.Error())
// test verbose off
SetVerboseDefault(false)
s := e.Error()
assert.Equal(t, Message(e), s)
assert.Equal(t, "yikes", s)
}
func TestMerryErr_Error(t *testing.T) {
origVerbose := verbose
defer func() {
verbose = origVerbose
}()
// test with verbose on
verbose = false
tests := []struct {
desc string
verbose bool
message, userMessage string
expected string
}{
{
desc: "with message",
message: "blue",
expected: "blue",
},
{
desc: "with user message",
userMessage: "red",
expected: "red",
},
}
for _, test := range tests {
t.Log("error message tests: " + test.desc)
verbose = test.verbose
err := New(test.message).WithUserMessage(test.userMessage)
t.Log(err.Error())
assert.Equal(t, test.expected, err.Error())
}
}
func TestMerryErr_Format(t *testing.T) {
e := New("Hi")
assert.Equal(t, fmt.Sprintf("%v", e), e.Error())
assert.Equal(t, fmt.Sprintf("%s", e), e.Error())
assert.Equal(t, fmt.Sprintf("%q", e), fmt.Sprintf("%q", e.Error()))
assert.Equal(t, fmt.Sprintf("%+v", e), Details(e))
}
func BenchmarkNew_withStackCapture(b *testing.B) {
for i := 0; i < b.N; i++ {
New("boom")
}
}
func BenchmarkNew_withoutStackCapture(b *testing.B) {
SetStackCaptureEnabled(false)
for i := 0; i < b.N; i++ {
New("boom")
}
}

68
vendor/github.com/ansel1/merry/print.go generated vendored Normal file
View File

@@ -0,0 +1,68 @@
package merry
import (
"bytes"
"fmt"
"runtime"
)
// Location returns zero values if e has no stacktrace
func Location(e error) (file string, line int) {
s := Stack(e)
if len(s) > 0 {
fnc := runtime.FuncForPC(s[0])
if fnc != nil {
return fnc.FileLine(s[0])
}
}
return "", 0
}
// SourceLine returns the string representation of
// Location's result or an empty string if there's
// no stracktrace.
func SourceLine(e error) string {
file, line := Location(e)
if line != 0 {
return fmt.Sprintf("%s:%d", file, line)
}
return ""
}
// Stacktrace returns the error's stacktrace as a string formatted
// the same way as golangs runtime package.
// If e has no stacktrace, returns an empty string.
func Stacktrace(e error) string {
s := Stack(e)
if len(s) > 0 {
buf := bytes.Buffer{}
for _, fp := range s {
fnc := runtime.FuncForPC(fp)
if fnc != nil {
f, l := fnc.FileLine(fp)
buf.WriteString(fnc.Name())
buf.WriteString(fmt.Sprintf("\n\t%s:%d\n", f, l))
}
}
return buf.String()
}
return ""
}
// Details returns e.Error() and e's stacktrace and user message, if set.
func Details(e error) string {
if e == nil {
return ""
}
msg := Message(e)
userMsg := UserMessage(e)
if userMsg != "" {
msg = fmt.Sprintf("%s\n\nUser Message: %s", msg, userMsg)
}
s := Stacktrace(e)
if s != "" {
msg += "\n\n" + s
}
return msg
}

14
vendor/github.com/asaskevich/govalidator/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,14 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- tip
notifications:
email:
- bwatas@gmail.com

View File

@@ -0,0 +1,26 @@
#### Support
If you do have a contribution to the package, feel free to create a Pull Request or an Issue.
#### What to contribute
If you don't know what to do, there are some features and functions that need to be done
- [ ] Refactor code
- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check
- [ ] Create actual list of contributors and projects that currently using this package
- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues)
- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions)
- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new
- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc
- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224)
- [ ] Implement fuzzing testing
- [ ] Implement some struct/map/array utilities
- [ ] Implement map/array validation
- [ ] Implement benchmarking
- [ ] Implement batch of examples
- [ ] Look at forks for new features and fixes
#### Advice
Feel free to create what you want, but keep in mind when you implement new features:
- Code must be clear and readable, names of variables/constants clearly describes what they are doing
- Public functions must be documented and described in source file and added to README.md to the list of available functions
- There are must be unit-tests for any new functions and improvements

21
vendor/github.com/asaskevich/govalidator/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Alex Saskevich
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.

449
vendor/github.com/asaskevich/govalidator/README.md generated vendored Normal file
View File

@@ -0,0 +1,449 @@
govalidator
===========
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) [![Go Report Card](https://goreportcard.com/badge/github.com/asaskevich/govalidator)](https://goreportcard.com/report/github.com/asaskevich/govalidator) [![GoSearch](http://go-search.org/badge?id=github.com%2Fasaskevich%2Fgovalidator)](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator)
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js).
#### Installation
Make sure that Go is installed on your computer.
Type the following command in your terminal:
go get github.com/asaskevich/govalidator
or you can get specified release of the package with `gopkg.in`:
go get gopkg.in/asaskevich/govalidator.v4
After it the package is ready to use.
#### Import package in your project
Add following line in your `*.go` file:
```go
import "github.com/asaskevich/govalidator"
```
If you are unhappy to use long `govalidator`, you can do something like this:
```go
import (
valid "github.com/asaskevich/govalidator"
)
```
#### Activate behavior to require all fields have a validation tag by default
`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function.
```go
import "github.com/asaskevich/govalidator"
func init() {
govalidator.SetFieldsRequiredByDefault(true)
}
```
Here's some code to explain it:
```go
// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
type exampleStruct struct {
Name string ``
Email string `valid:"email"`
}
// this, however, will only fail when Email is empty or an invalid email address:
type exampleStruct2 struct {
Name string `valid:"-"`
Email string `valid:"email"`
}
// lastly, this will only fail when Email is an invalid email address but not when it's empty:
type exampleStruct2 struct {
Name string `valid:"-"`
Email string `valid:"email,optional"`
}
```
#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123))
##### Custom validator function signature
A context was added as the second parameter, for structs this is the object being validated this makes dependent validation possible.
```go
import "github.com/asaskevich/govalidator"
// old signature
func(i interface{}) bool
// new signature
func(i interface{}, o interface{}) bool
```
##### Adding a custom validator
This was changed to prevent data races when accessing custom validators.
```go
import "github.com/asaskevich/govalidator"
// before
govalidator.CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool {
// ...
})
// after
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool {
// ...
}))
```
#### List of functions:
```go
func Abs(value float64) float64
func BlackList(str, chars string) string
func ByteLength(str string, params ...string) bool
func CamelCaseToUnderscore(str string) string
func Contains(str, substring string) bool
func Count(array []interface{}, iterator ConditionIterator) int
func Each(array []interface{}, iterator Iterator)
func ErrorByField(e error, field string) string
func ErrorsByField(e error) map[string]string
func Filter(array []interface{}, iterator ConditionIterator) []interface{}
func Find(array []interface{}, iterator ConditionIterator) interface{}
func GetLine(s string, index int) (string, error)
func GetLines(s string) []string
func InRange(value, left, right float64) bool
func IsASCII(str string) bool
func IsAlpha(str string) bool
func IsAlphanumeric(str string) bool
func IsBase64(str string) bool
func IsByteLength(str string, min, max int) bool
func IsCIDR(str string) bool
func IsCreditCard(str string) bool
func IsDNSName(str string) bool
func IsDataURI(str string) bool
func IsDialString(str string) bool
func IsDivisibleBy(str, num string) bool
func IsEmail(str string) bool
func IsFilePath(str string) (bool, int)
func IsFloat(str string) bool
func IsFullWidth(str string) bool
func IsHalfWidth(str string) bool
func IsHexadecimal(str string) bool
func IsHexcolor(str string) bool
func IsHost(str string) bool
func IsIP(str string) bool
func IsIPv4(str string) bool
func IsIPv6(str string) bool
func IsISBN(str string, version int) bool
func IsISBN10(str string) bool
func IsISBN13(str string) bool
func IsISO3166Alpha2(str string) bool
func IsISO3166Alpha3(str string) bool
func IsISO693Alpha2(str string) bool
func IsISO693Alpha3b(str string) bool
func IsISO4217(str string) bool
func IsIn(str string, params ...string) bool
func IsInt(str string) bool
func IsJSON(str string) bool
func IsLatitude(str string) bool
func IsLongitude(str string) bool
func IsLowerCase(str string) bool
func IsMAC(str string) bool
func IsMongoID(str string) bool
func IsMultibyte(str string) bool
func IsNatural(value float64) bool
func IsNegative(value float64) bool
func IsNonNegative(value float64) bool
func IsNonPositive(value float64) bool
func IsNull(str string) bool
func IsNumeric(str string) bool
func IsPort(str string) bool
func IsPositive(value float64) bool
func IsPrintableASCII(str string) bool
func IsRFC3339(str string) bool
func IsRFC3339WithoutZone(str string) bool
func IsRGBcolor(str string) bool
func IsRequestURI(rawurl string) bool
func IsRequestURL(rawurl string) bool
func IsSSN(str string) bool
func IsSemver(str string) bool
func IsTime(str string, format string) bool
func IsURL(str string) bool
func IsUTFDigit(str string) bool
func IsUTFLetter(str string) bool
func IsUTFLetterNumeric(str string) bool
func IsUTFNumeric(str string) bool
func IsUUID(str string) bool
func IsUUIDv3(str string) bool
func IsUUIDv4(str string) bool
func IsUUIDv5(str string) bool
func IsUpperCase(str string) bool
func IsVariableWidth(str string) bool
func IsWhole(value float64) bool
func LeftTrim(str, chars string) string
func Map(array []interface{}, iterator ResultIterator) []interface{}
func Matches(str, pattern string) bool
func NormalizeEmail(str string) (string, error)
func PadBoth(str string, padStr string, padLen int) string
func PadLeft(str string, padStr string, padLen int) string
func PadRight(str string, padStr string, padLen int) string
func Range(str string, params ...string) bool
func RemoveTags(s string) string
func ReplacePattern(str, pattern, replace string) string
func Reverse(s string) string
func RightTrim(str, chars string) string
func RuneLength(str string, params ...string) bool
func SafeFileName(str string) string
func SetFieldsRequiredByDefault(value bool)
func Sign(value float64) float64
func StringLength(str string, params ...string) bool
func StringMatches(s string, params ...string) bool
func StripLow(str string, keepNewLines bool) string
func ToBoolean(str string) (bool, error)
func ToFloat(str string) (float64, error)
func ToInt(str string) (int64, error)
func ToJSON(obj interface{}) (string, error)
func ToString(obj interface{}) string
func Trim(str, chars string) string
func Truncate(str string, length int, ending string) string
func UnderscoreToCamelCase(s string) string
func ValidateStruct(s interface{}) (bool, error)
func WhiteList(str, chars string) string
type ConditionIterator
type CustomTypeValidator
type Error
func (e Error) Error() string
type Errors
func (es Errors) Error() string
func (es Errors) Errors() []error
type ISO3166Entry
type Iterator
type ParamValidator
type ResultIterator
type UnsupportedTypeError
func (e *UnsupportedTypeError) Error() string
type Validator
```
#### Examples
###### IsURL
```go
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`))
```
###### ToString
```go
type User struct {
FirstName string
LastName string
}
str := govalidator.ToString(&User{"John", "Juan"})
println(str)
```
###### Each, Map, Filter, Count for slices
Each iterates over the slice/array and calls Iterator for every item
```go
data := []interface{}{1, 2, 3, 4, 5}
var fn govalidator.Iterator = func(value interface{}, index int) {
println(value.(int))
}
govalidator.Each(data, fn)
```
```go
data := []interface{}{1, 2, 3, 4, 5}
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} {
return value.(int) * 3
}
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
```
```go
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool {
return value.(int)%2 == 0
}
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
_ = govalidator.Count(data, fn) // result = 5
```
###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2)
If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this:
```go
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
return str == "duck"
})
```
For completely custom validators (interface-based), see below.
Here is a list of available validators for struct fields (validator - used function):
```go
"email": IsEmail,
"url": IsURL,
"dialstring": IsDialString,
"requrl": IsRequestURL,
"requri": IsRequestURI,
"alpha": IsAlpha,
"utfletter": IsUTFLetter,
"alphanum": IsAlphanumeric,
"utfletternum": IsUTFLetterNumeric,
"numeric": IsNumeric,
"utfnumeric": IsUTFNumeric,
"utfdigit": IsUTFDigit,
"hexadecimal": IsHexadecimal,
"hexcolor": IsHexcolor,
"rgbcolor": IsRGBcolor,
"lowercase": IsLowerCase,
"uppercase": IsUpperCase,
"int": IsInt,
"float": IsFloat,
"null": IsNull,
"uuid": IsUUID,
"uuidv3": IsUUIDv3,
"uuidv4": IsUUIDv4,
"uuidv5": IsUUIDv5,
"creditcard": IsCreditCard,
"isbn10": IsISBN10,
"isbn13": IsISBN13,
"json": IsJSON,
"multibyte": IsMultibyte,
"ascii": IsASCII,
"printableascii": IsPrintableASCII,
"fullwidth": IsFullWidth,
"halfwidth": IsHalfWidth,
"variablewidth": IsVariableWidth,
"base64": IsBase64,
"datauri": IsDataURI,
"ip": IsIP,
"port": IsPort,
"ipv4": IsIPv4,
"ipv6": IsIPv6,
"dns": IsDNSName,
"host": IsHost,
"mac": IsMAC,
"latitude": IsLatitude,
"longitude": IsLongitude,
"ssn": IsSSN,
"semver": IsSemver,
"rfc3339": IsRFC3339,
"rfc3339WithoutZone": IsRFC3339WithoutZone,
"ISO3166Alpha2": IsISO3166Alpha2,
"ISO3166Alpha3": IsISO3166Alpha3,
```
Validators with parameters
```go
"range(min|max)": Range,
"length(min|max)": ByteLength,
"runelength(min|max)": RuneLength,
"matches(pattern)": StringMatches,
"in(string1|string2|...|stringN)": IsIn,
```
And here is small example of usage:
```go
type Post struct {
Title string `valid:"alphanum,required"`
Message string `valid:"duck,ascii"`
AuthorIP string `valid:"ipv4"`
Date string `valid:"-"`
}
post := &Post{
Title: "My Example Post",
Message: "duck",
AuthorIP: "123.234.54.3",
}
// Add your own struct validation tags
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
return str == "duck"
})
result, err := govalidator.ValidateStruct(post)
if err != nil {
println("error: " + err.Error())
}
println(result)
```
###### WhiteList
```go
// Remove all characters from string ignoring characters between "a" and "z"
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
```
###### Custom validation functions
Custom validation using your own domain specific validators is also available - here's an example of how to use it:
```go
import "github.com/asaskevich/govalidator"
type CustomByteArray [6]byte // custom types are supported and can be validated
type StructWithCustomByteArray struct {
ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence
Email string `valid:"email"`
CustomMinLength int `valid:"-"`
}
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
switch v := context.(type) { // you can type switch on the context interface being validated
case StructWithCustomByteArray:
// you can check and validate against some other field in the context,
// return early or not validate against the context at all your choice
case SomeOtherType:
// ...
default:
// expecting some other type? Throw/panic here or continue
}
switch v := i.(type) { // type switch on the struct field being validated
case CustomByteArray:
for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes
if e != 0 {
return true
}
}
}
return false
}))
govalidator.CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation
case StructWithCustomByteArray:
return len(v.ID) >= v.CustomMinLength
}
return false
}))
```
#### Notes
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator).
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator).
#### Support
If you do have a contribution to the package, feel free to create a Pull Request or an Issue.
#### What to contribute
If you don't know what to do, there are some features and functions that need to be done
- [ ] Refactor code
- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check
- [ ] Create actual list of contributors and projects that currently using this package
- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues)
- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions)
- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new
- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc
- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224)
- [ ] Implement fuzzing testing
- [ ] Implement some struct/map/array utilities
- [ ] Implement map/array validation
- [ ] Implement benchmarking
- [ ] Implement batch of examples
- [ ] Look at forks for new features and fixes
#### Advice
Feel free to create what you want, but keep in mind when you implement new features:
- Code must be clear and readable, names of variables/constants clearly describes what they are doing
- Public functions must be documented and described in source file and added to README.md to the list of available functions
- There are must be unit-tests for any new functions and improvements
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors)
* [Daniel Lohse](https://github.com/annismckenzie)
* [Attila Oláh](https://github.com/attilaolah)
* [Daniel Korner](https://github.com/Dadie)
* [Steven Wilkin](https://github.com/stevenwilkin)
* [Deiwin Sarjas](https://github.com/deiwin)
* [Noah Shibley](https://github.com/slugmobile)
* [Nathan Davies](https://github.com/nathj07)
* [Matt Sanford](https://github.com/mzsanford)
* [Simon ccl1115](https://github.com/ccl1115)

58
vendor/github.com/asaskevich/govalidator/arrays.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
package govalidator
// Iterator is the function that accepts element of slice/array and its index
type Iterator func(interface{}, int)
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
type ResultIterator func(interface{}, int) interface{}
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
type ConditionIterator func(interface{}, int) bool
// Each iterates over the slice and apply Iterator to every item
func Each(array []interface{}, iterator Iterator) {
for index, data := range array {
iterator(data, index)
}
}
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
func Map(array []interface{}, iterator ResultIterator) []interface{} {
var result = make([]interface{}, len(array))
for index, data := range array {
result[index] = iterator(data, index)
}
return result
}
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
func Find(array []interface{}, iterator ConditionIterator) interface{} {
for index, data := range array {
if iterator(data, index) {
return data
}
}
return nil
}
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
var result = make([]interface{}, 0)
for index, data := range array {
if iterator(data, index) {
result = append(result, data)
}
}
return result
}
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
func Count(array []interface{}, iterator ConditionIterator) int {
count := 0
for index, data := range array {
if iterator(data, index) {
count = count + 1
}
}
return count
}

116
vendor/github.com/asaskevich/govalidator/arrays_test.go generated vendored Normal file
View File

@@ -0,0 +1,116 @@
package govalidator
import "testing"
func TestEach(t *testing.T) {
// TODO Maybe refactor?
t.Parallel()
acc := 0
data := []interface{}{1, 2, 3, 4, 5}
var fn Iterator = func(value interface{}, index int) {
acc = acc + value.(int)
}
Each(data, fn)
if acc != 15 {
t.Errorf("Expected Each(..) to be %v, got %v", 15, acc)
}
}
func ExampleEach() {
data := []interface{}{1, 2, 3, 4, 5}
var fn Iterator = func(value interface{}, index int) {
println(value.(int))
}
Each(data, fn)
}
func TestMap(t *testing.T) {
// TODO Maybe refactor?
t.Parallel()
data := []interface{}{1, 2, 3, 4, 5}
var fn ResultIterator = func(value interface{}, index int) interface{} {
return value.(int) * 3
}
result := Map(data, fn)
for i, d := range result {
if d != fn(data[i], i) {
t.Errorf("Expected Map(..) to be %v, got %v", fn(data[i], i), d)
}
}
}
func ExampleMap() {
data := []interface{}{1, 2, 3, 4, 5}
var fn ResultIterator = func(value interface{}, index int) interface{} {
return value.(int) * 3
}
_ = Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
}
func TestFind(t *testing.T) {
// TODO Maybe refactor?
t.Parallel()
findElement := 96
data := []interface{}{1, 2, 3, 4, findElement, 5}
var fn1 ConditionIterator = func(value interface{}, index int) bool {
return value.(int) == findElement
}
var fn2 ConditionIterator = func(value interface{}, index int) bool {
value, _ = value.(string)
return value == "govalidator"
}
val1 := Find(data, fn1)
val2 := Find(data, fn2)
if val1 != findElement {
t.Errorf("Expected Find(..) to be %v, got %v", findElement, val1)
}
if val2 != nil {
t.Errorf("Expected Find(..) to be %v, got %v", nil, val2)
}
}
func TestFilter(t *testing.T) {
// TODO Maybe refactor?
t.Parallel()
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
answer := []interface{}{2, 4, 6, 8, 10}
var fn ConditionIterator = func(value interface{}, index int) bool {
return value.(int)%2 == 0
}
result := Filter(data, fn)
for i := range result {
if result[i] != answer[i] {
t.Errorf("Expected Filter(..) to be %v, got %v", answer[i], result[i])
}
}
}
func ExampleFilter() {
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var fn ConditionIterator = func(value interface{}, index int) bool {
return value.(int)%2 == 0
}
_ = Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
}
func TestCount(t *testing.T) {
// TODO Maybe refactor?
t.Parallel()
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
count := 5
var fn ConditionIterator = func(value interface{}, index int) bool {
return value.(int)%2 == 0
}
result := Count(data, fn)
if result != count {
t.Errorf("Expected Count(..) to be %v, got %v", count, result)
}
}
func ExampleCount() {
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var fn ConditionIterator = func(value interface{}, index int) bool {
return value.(int)%2 == 0
}
_ = Count(data, fn) // result = 5
}

45
vendor/github.com/asaskevich/govalidator/converter.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package govalidator
import (
"encoding/json"
"fmt"
"strconv"
)
// ToString convert the input to a string.
func ToString(obj interface{}) string {
res := fmt.Sprintf("%v", obj)
return string(res)
}
// ToJSON convert the input to a valid JSON string
func ToJSON(obj interface{}) (string, error) {
res, err := json.Marshal(obj)
if err != nil {
res = []byte("")
}
return string(res), err
}
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
func ToFloat(str string) (float64, error) {
res, err := strconv.ParseFloat(str, 64)
if err != nil {
res = 0.0
}
return res, err
}
// ToInt convert the input string to an integer, or 0 if the input is not an integer.
func ToInt(str string) (int64, error) {
res, err := strconv.ParseInt(str, 0, 64)
if err != nil {
res = 0
}
return res, err
}
// ToBoolean convert the input string to a boolean.
func ToBoolean(str string) (bool, error) {
return strconv.ParseBool(str)
}

View File

@@ -0,0 +1,78 @@
package govalidator
import (
"fmt"
"testing"
)
func TestToInt(t *testing.T) {
tests := []string{"1000", "-123", "abcdef", "100000000000000000000000000000000000000000000"}
expected := []int64{1000, -123, 0, 0}
for i := 0; i < len(tests); i++ {
result, _ := ToInt(tests[i])
if result != expected[i] {
t.Log("Case ", i, ": expected ", expected[i], " when result is ", result)
t.FailNow()
}
}
}
func TestToBoolean(t *testing.T) {
tests := []string{"true", "1", "True", "false", "0", "abcdef"}
expected := []bool{true, true, true, false, false, false}
for i := 0; i < len(tests); i++ {
res, _ := ToBoolean(tests[i])
if res != expected[i] {
t.Log("Case ", i, ": expected ", expected[i], " when result is ", res)
t.FailNow()
}
}
}
func toString(t *testing.T, test interface{}, expected string) {
res := ToString(test)
if res != expected {
t.Log("Case ToString: expected ", expected, " when result is ", res)
t.FailNow()
}
}
func TestToString(t *testing.T) {
toString(t, "str123", "str123")
toString(t, 123, "123")
toString(t, 12.3, "12.3")
toString(t, true, "true")
toString(t, 1.5+10i, "(1.5+10i)")
// Sprintf function not guarantee that maps with equal keys always will be equal in string representation
//toString(t, struct{ Keys map[int]int }{Keys: map[int]int{1: 2, 3: 4}}, "{map[1:2 3:4]}")
}
func TestToFloat(t *testing.T) {
tests := []string{"", "123", "-.01", "10.", "string", "1.23e3", ".23e10"}
expected := []float64{0, 123, -0.01, 10.0, 0, 1230, 0.23e10}
for i := 0; i < len(tests); i++ {
res, _ := ToFloat(tests[i])
if res != expected[i] {
t.Log("Case ", i, ": expected ", expected[i], " when result is ", res)
t.FailNow()
}
}
}
func TestToJSON(t *testing.T) {
tests := []interface{}{"test", map[string]string{"a": "b", "b": "c"}, func() error { return fmt.Errorf("Error") }}
expected := [][]string{
{"\"test\"", "<nil>"},
{"{\"a\":\"b\",\"b\":\"c\"}", "<nil>"},
{"", "json: unsupported type: func() error"},
}
for i, test := range tests {
actual, err := ToJSON(test)
if actual != expected[i][0] {
t.Errorf("Expected toJSON(%v) to return '%v', got '%v'", test, expected[i][0], actual)
}
if fmt.Sprintf("%v", err) != expected[i][1] {
t.Errorf("Expected error returned from toJSON(%v) to return '%v', got '%v'", test, expected[i][1], fmt.Sprintf("%v", err))
}
}
}

36
vendor/github.com/asaskevich/govalidator/error.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
package govalidator
import "strings"
// Errors is an array of multiple errors and conforms to the error interface.
type Errors []error
// Errors returns itself.
func (es Errors) Errors() []error {
return es
}
func (es Errors) Error() string {
var errs []string
for _, e := range es {
errs = append(errs, e.Error())
}
return strings.Join(errs, ";")
}
// Error encapsulates a name, an error and whether there's a custom error message or not.
type Error struct {
Name string
Err error
CustomErrorMessageExists bool
// Validator indicates the name of the validator that failed
Validator string
}
func (e Error) Error() string {
if e.CustomErrorMessageExists {
return e.Err.Error()
}
return e.Name + ": " + e.Err.Error()
}

29
vendor/github.com/asaskevich/govalidator/error_test.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package govalidator
import (
"fmt"
"testing"
)
func TestErrorsToString(t *testing.T) {
t.Parallel()
customErr := &Error{Name: "Custom Error Name", Err: fmt.Errorf("stdlib error")}
customErrWithCustomErrorMessage := &Error{Name: "Custom Error Name 2", Err: fmt.Errorf("Bad stuff happened"), CustomErrorMessageExists: true}
var tests = []struct {
param1 Errors
expected string
}{
{Errors{}, ""},
{Errors{fmt.Errorf("Error 1")}, "Error 1"},
{Errors{fmt.Errorf("Error 1"), fmt.Errorf("Error 2")}, "Error 1;Error 2"},
{Errors{customErr, fmt.Errorf("Error 2")}, "Custom Error Name: stdlib error;Error 2"},
{Errors{fmt.Errorf("Error 123"), customErrWithCustomErrorMessage}, "Error 123;Bad stuff happened"},
}
for _, test := range tests {
actual := test.param1.Error()
if actual != test.expected {
t.Errorf("Expected Error() to return '%v', got '%v'", test.expected, actual)
}
}
}

94
vendor/github.com/asaskevich/govalidator/numerics.go generated vendored Normal file
View File

@@ -0,0 +1,94 @@
package govalidator
import (
"math"
"reflect"
)
// Abs returns absolute value of number
func Abs(value float64) float64 {
return math.Abs(value)
}
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
func Sign(value float64) float64 {
if value > 0 {
return 1
} else if value < 0 {
return -1
} else {
return 0
}
}
// IsNegative returns true if value < 0
func IsNegative(value float64) bool {
return value < 0
}
// IsPositive returns true if value > 0
func IsPositive(value float64) bool {
return value > 0
}
// IsNonNegative returns true if value >= 0
func IsNonNegative(value float64) bool {
return value >= 0
}
// IsNonPositive returns true if value <= 0
func IsNonPositive(value float64) bool {
return value <= 0
}
// InRange returns true if value lies between left and right border
func InRangeInt(value, left, right int) bool {
if left > right {
left, right = right, left
}
return value >= left && value <= right
}
// InRange returns true if value lies between left and right border
func InRangeFloat32(value, left, right float32) bool {
if left > right {
left, right = right, left
}
return value >= left && value <= right
}
// InRange returns true if value lies between left and right border
func InRangeFloat64(value, left, right float64) bool {
if left > right {
left, right = right, left
}
return value >= left && value <= right
}
// InRange returns true if value lies between left and right border, generic type to handle int, float32 or float64, all types must the same type
func InRange(value interface{}, left interface{}, right interface{}) bool {
reflectValue := reflect.TypeOf(value).Kind()
reflectLeft := reflect.TypeOf(left).Kind()
reflectRight := reflect.TypeOf(right).Kind()
if reflectValue == reflect.Int && reflectLeft == reflect.Int && reflectRight == reflect.Int {
return InRangeInt(value.(int), left.(int), right.(int))
} else if reflectValue == reflect.Float32 && reflectLeft == reflect.Float32 && reflectRight == reflect.Float32 {
return InRangeFloat32(value.(float32), left.(float32), right.(float32))
} else if reflectValue == reflect.Float64 && reflectLeft == reflect.Float64 && reflectRight == reflect.Float64 {
return InRangeFloat64(value.(float64), left.(float64), right.(float64))
} else {
return false
}
}
// IsWhole returns true if value is whole number
func IsWhole(value float64) bool {
return math.Remainder(value, 1) == 0
}
// IsNatural returns true if value is natural number (positive and whole)
func IsNatural(value float64) bool {
return IsWhole(value) && IsPositive(value)
}

View File

@@ -0,0 +1,349 @@
package govalidator
import "testing"
func TestAbs(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
expected float64
}{
{0, 0},
{-1, 1},
{10, 10},
{3.14, 3.14},
{-96, 96},
{-10e-12, 10e-12},
}
for _, test := range tests {
actual := Abs(test.param)
if actual != test.expected {
t.Errorf("Expected Abs(%v) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestSign(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
expected float64
}{
{0, 0},
{-1, -1},
{10, 1},
{3.14, 1},
{-96, -1},
{-10e-12, -1},
}
for _, test := range tests {
actual := Sign(test.param)
if actual != test.expected {
t.Errorf("Expected Sign(%v) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestIsNegative(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
expected bool
}{
{0, false},
{-1, true},
{10, false},
{3.14, false},
{-96, true},
{-10e-12, true},
}
for _, test := range tests {
actual := IsNegative(test.param)
if actual != test.expected {
t.Errorf("Expected IsNegative(%v) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestIsNonNegative(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
expected bool
}{
{0, true},
{-1, false},
{10, true},
{3.14, true},
{-96, false},
{-10e-12, false},
}
for _, test := range tests {
actual := IsNonNegative(test.param)
if actual != test.expected {
t.Errorf("Expected IsNonNegative(%v) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestIsPositive(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
expected bool
}{
{0, false},
{-1, false},
{10, true},
{3.14, true},
{-96, false},
{-10e-12, false},
}
for _, test := range tests {
actual := IsPositive(test.param)
if actual != test.expected {
t.Errorf("Expected IsPositive(%v) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestIsNonPositive(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
expected bool
}{
{0, true},
{-1, true},
{10, false},
{3.14, false},
{-96, true},
{-10e-12, true},
}
for _, test := range tests {
actual := IsNonPositive(test.param)
if actual != test.expected {
t.Errorf("Expected IsNonPositive(%v) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestIsWhole(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
expected bool
}{
{0, true},
{-1, true},
{10, true},
{3.14, false},
{-96, true},
{-10e-12, false},
}
for _, test := range tests {
actual := IsWhole(test.param)
if actual != test.expected {
t.Errorf("Expected IsWhole(%v) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestIsNatural(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
expected bool
}{
{0, false},
{-1, false},
{10, true},
{3.14, false},
{96, true},
{-10e-12, false},
}
for _, test := range tests {
actual := IsNatural(test.param)
if actual != test.expected {
t.Errorf("Expected IsNatural(%v) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestInRangeInt(t *testing.T) {
t.Parallel()
var tests = []struct {
param int
left int
right int
expected bool
}{
{0, 0, 0, true},
{1, 0, 0, false},
{-1, 0, 0, false},
{0, -1, 1, true},
{0, 0, 1, true},
{0, -1, 0, true},
{0, 0, -1, true},
{0, 10, 5, false},
}
for _, test := range tests {
actual := InRangeInt(test.param, test.left, test.right)
if actual != test.expected {
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
}
}
}
func TestInRangeFloat32(t *testing.T) {
t.Parallel()
var tests = []struct {
param float32
left float32
right float32
expected bool
}{
{0, 0, 0, true},
{1, 0, 0, false},
{-1, 0, 0, false},
{0, -1, 1, true},
{0, 0, 1, true},
{0, -1, 0, true},
{0, 0, -1, true},
{0, 10, 5, false},
}
for _, test := range tests {
actual := InRangeFloat32(test.param, test.left, test.right)
if actual != test.expected {
t.Errorf("Expected InRangeFloat32(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
}
}
}
func TestInRangeFloat64(t *testing.T) {
t.Parallel()
var tests = []struct {
param float64
left float64
right float64
expected bool
}{
{0, 0, 0, true},
{1, 0, 0, false},
{-1, 0, 0, false},
{0, -1, 1, true},
{0, 0, 1, true},
{0, -1, 0, true},
{0, 0, -1, true},
{0, 10, 5, false},
}
for _, test := range tests {
actual := InRangeFloat64(test.param, test.left, test.right)
if actual != test.expected {
t.Errorf("Expected InRangeFloat64(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
}
}
}
func TestInRange(t *testing.T) {
t.Parallel()
var testsInt = []struct {
param int
left int
right int
expected bool
}{
{0, 0, 0, true},
{1, 0, 0, false},
{-1, 0, 0, false},
{0, -1, 1, true},
{0, 0, 1, true},
{0, -1, 0, true},
{0, 0, -1, true},
{0, 10, 5, false},
}
for _, test := range testsInt {
actual := InRange(test.param, test.left, test.right)
if actual != test.expected {
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
}
}
var testsFloat32 = []struct {
param float32
left float32
right float32
expected bool
}{
{0, 0, 0, true},
{1, 0, 0, false},
{-1, 0, 0, false},
{0, -1, 1, true},
{0, 0, 1, true},
{0, -1, 0, true},
{0, 0, -1, true},
{0, 10, 5, false},
}
for _, test := range testsFloat32 {
actual := InRange(test.param, test.left, test.right)
if actual != test.expected {
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
}
}
var testsFloat64 = []struct {
param float64
left float64
right float64
expected bool
}{
{0, 0, 0, true},
{1, 0, 0, false},
{-1, 0, 0, false},
{0, -1, 1, true},
{0, 0, 1, true},
{0, -1, 0, true},
{0, 0, -1, true},
{0, 10, 5, false},
}
for _, test := range testsFloat64 {
actual := InRange(test.param, test.left, test.right)
if actual != test.expected {
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
}
}
var testsTypeMix = []struct {
param int
left float64
right float64
expected bool
}{
{0, 0, 0, false},
{1, 0, 0, false},
{-1, 0, 0, false},
{0, -1, 1, false},
{0, 0, 1, false},
{0, -1, 0, false},
{0, 0, -1, false},
{0, 10, 5, false},
}
for _, test := range testsTypeMix {
actual := InRange(test.param, test.left, test.right)
if actual != test.expected {
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
}
}
}

90
vendor/github.com/asaskevich/govalidator/patterns.go generated vendored Normal file
View File

@@ -0,0 +1,90 @@
package govalidator
import "regexp"
// Basic regular expressions for validating strings
const (
Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$"
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
ISBN13 string = "^(?:[0-9]{13})$"
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
Alpha string = "^[a-zA-Z]+$"
Alphanumeric string = "^[a-zA-Z0-9]+$"
Numeric string = "^[0-9]+$"
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
Hexadecimal string = "^[0-9a-fA-F]+$"
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
ASCII string = "^[\x00-\x7F]+$"
Multibyte string = "[^\x00-\x7F]"
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
PrintableASCII string = "^[\x20-\x7E]+$"
DataURI string = "^data:.+\\/(.+);base64$"
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)`
URLUsername string = `(\S+(:\S*)?@)`
URLPath string = `((\/|\?|#)[^\s]*)`
URLPort string = `(:(\d{1,5}))`
URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))`
URLSubdomain string = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))`
URL string = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
UnixPath string = `^(/[^/\x00]*)+/?$`
Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$"
tagName string = "valid"
)
// Used by IsFilePath func
const (
// Unknown is unresolved OS type
Unknown = iota
// Win is Windows type
Win
// Unix is *nix OS types
Unix
)
var (
rxEmail = regexp.MustCompile(Email)
rxCreditCard = regexp.MustCompile(CreditCard)
rxISBN10 = regexp.MustCompile(ISBN10)
rxISBN13 = regexp.MustCompile(ISBN13)
rxUUID3 = regexp.MustCompile(UUID3)
rxUUID4 = regexp.MustCompile(UUID4)
rxUUID5 = regexp.MustCompile(UUID5)
rxUUID = regexp.MustCompile(UUID)
rxAlpha = regexp.MustCompile(Alpha)
rxAlphanumeric = regexp.MustCompile(Alphanumeric)
rxNumeric = regexp.MustCompile(Numeric)
rxInt = regexp.MustCompile(Int)
rxFloat = regexp.MustCompile(Float)
rxHexadecimal = regexp.MustCompile(Hexadecimal)
rxHexcolor = regexp.MustCompile(Hexcolor)
rxRGBcolor = regexp.MustCompile(RGBcolor)
rxASCII = regexp.MustCompile(ASCII)
rxPrintableASCII = regexp.MustCompile(PrintableASCII)
rxMultibyte = regexp.MustCompile(Multibyte)
rxFullWidth = regexp.MustCompile(FullWidth)
rxHalfWidth = regexp.MustCompile(HalfWidth)
rxBase64 = regexp.MustCompile(Base64)
rxDataURI = regexp.MustCompile(DataURI)
rxLatitude = regexp.MustCompile(Latitude)
rxLongitude = regexp.MustCompile(Longitude)
rxDNSName = regexp.MustCompile(DNSName)
rxURL = regexp.MustCompile(URL)
rxSSN = regexp.MustCompile(SSN)
rxWinPath = regexp.MustCompile(WinPath)
rxUnixPath = regexp.MustCompile(UnixPath)
rxSemver = regexp.MustCompile(Semver)
)

616
vendor/github.com/asaskevich/govalidator/types.go generated vendored Normal file
View File

@@ -0,0 +1,616 @@
package govalidator
import (
"reflect"
"regexp"
"sync"
)
// Validator is a wrapper for a validator function that returns bool and accepts string.
type Validator func(str string) bool
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
type CustomTypeValidator func(i interface{}, o interface{}) bool
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
type ParamValidator func(str string, params ...string) bool
type tagOptionsMap map[string]string
// UnsupportedTypeError is a wrapper for reflect.Type
type UnsupportedTypeError struct {
Type reflect.Type
}
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
// It implements the methods to sort by string.
type stringValues []reflect.Value
// ParamTagMap is a map of functions accept variants parameters
var ParamTagMap = map[string]ParamValidator{
"length": ByteLength,
"range": Range,
"runelength": RuneLength,
"stringlength": StringLength,
"matches": StringMatches,
"in": isInRaw,
"rsapub": IsRsaPub,
}
// ParamTagRegexMap maps param tags to their respective regexes.
var ParamTagRegexMap = map[string]*regexp.Regexp{
"range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"),
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
"runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"),
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
"in": regexp.MustCompile(`^in\((.*)\)`),
"matches": regexp.MustCompile(`^matches\((.+)\)$`),
"rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"),
}
type customTypeTagMap struct {
validators map[string]CustomTypeValidator
sync.RWMutex
}
func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
tm.RLock()
defer tm.RUnlock()
v, ok := tm.validators[name]
return v, ok
}
func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
tm.Lock()
defer tm.Unlock()
tm.validators[name] = ctv
}
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
// `type UUID [16]byte` (this would be handled as an array of bytes).
var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
var TagMap = map[string]Validator{
"email": IsEmail,
"url": IsURL,
"dialstring": IsDialString,
"requrl": IsRequestURL,
"requri": IsRequestURI,
"alpha": IsAlpha,
"utfletter": IsUTFLetter,
"alphanum": IsAlphanumeric,
"utfletternum": IsUTFLetterNumeric,
"numeric": IsNumeric,
"utfnumeric": IsUTFNumeric,
"utfdigit": IsUTFDigit,
"hexadecimal": IsHexadecimal,
"hexcolor": IsHexcolor,
"rgbcolor": IsRGBcolor,
"lowercase": IsLowerCase,
"uppercase": IsUpperCase,
"int": IsInt,
"float": IsFloat,
"null": IsNull,
"uuid": IsUUID,
"uuidv3": IsUUIDv3,
"uuidv4": IsUUIDv4,
"uuidv5": IsUUIDv5,
"creditcard": IsCreditCard,
"isbn10": IsISBN10,
"isbn13": IsISBN13,
"json": IsJSON,
"multibyte": IsMultibyte,
"ascii": IsASCII,
"printableascii": IsPrintableASCII,
"fullwidth": IsFullWidth,
"halfwidth": IsHalfWidth,
"variablewidth": IsVariableWidth,
"base64": IsBase64,
"datauri": IsDataURI,
"ip": IsIP,
"port": IsPort,
"ipv4": IsIPv4,
"ipv6": IsIPv6,
"dns": IsDNSName,
"host": IsHost,
"mac": IsMAC,
"latitude": IsLatitude,
"longitude": IsLongitude,
"ssn": IsSSN,
"semver": IsSemver,
"rfc3339": IsRFC3339,
"rfc3339WithoutZone": IsRFC3339WithoutZone,
"ISO3166Alpha2": IsISO3166Alpha2,
"ISO3166Alpha3": IsISO3166Alpha3,
"ISO4217": IsISO4217,
}
// ISO3166Entry stores country codes
type ISO3166Entry struct {
EnglishShortName string
FrenchShortName string
Alpha2Code string
Alpha3Code string
Numeric string
}
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
var ISO3166List = []ISO3166Entry{
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
{"Albania", "Albanie (l')", "AL", "ALB", "008"},
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
{"Andorra", "Andorre (l')", "AD", "AND", "020"},
{"Angola", "Angola (l')", "AO", "AGO", "024"},
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
{"Australia", "Australie (l')", "AU", "AUS", "036"},
{"Austria", "Autriche (l')", "AT", "AUT", "040"},
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
{"Canada", "Canada (le)", "CA", "CAN", "124"},
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
{"Chad", "Tchad (le)", "TD", "TCD", "148"},
{"Chile", "Chili (le)", "CL", "CHL", "152"},
{"China", "Chine (la)", "CN", "CHN", "156"},
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
{"Colombia", "Colombie (la)", "CO", "COL", "170"},
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
{"Mayotte", "Mayotte", "YT", "MYT", "175"},
{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
{"Cuba", "Cuba", "CU", "CUB", "192"},
{"Cyprus", "Chypre", "CY", "CYP", "196"},
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
{"El Salvador", "El Salvador", "SV", "SLV", "222"},
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
{"Estonia", "Estonie (l')", "EE", "EST", "233"},
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
{"Finland", "Finlande (la)", "FI", "FIN", "246"},
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
{"France", "France (la)", "FR", "FRA", "250"},
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
{"Kiribati", "Kiribati", "KI", "KIR", "296"},
{"Greece", "Grèce (la)", "GR", "GRC", "300"},
{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
{"Guam", "Guam", "GU", "GUM", "316"},
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
{"Haiti", "Haïti", "HT", "HTI", "332"},
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
{"Honduras", "Honduras (le)", "HN", "HND", "340"},
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
{"Iceland", "Islande (l')", "IS", "ISL", "352"},
{"India", "Inde (l')", "IN", "IND", "356"},
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
{"Israel", "Israël", "IL", "ISR", "376"},
{"Italy", "Italie (l')", "IT", "ITA", "380"},
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
{"Japan", "Japon (le)", "JP", "JPN", "392"},
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
{"Libya", "Libye (la)", "LY", "LBY", "434"},
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
{"Macao", "Macao", "MO", "MAC", "446"},
{"Madagascar", "Madagascar", "MG", "MDG", "450"},
{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
{"Mali", "Mali (le)", "ML", "MLI", "466"},
{"Malta", "Malte", "MT", "MLT", "470"},
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
{"Mauritius", "Maurice", "MU", "MUS", "480"},
{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
{"Monaco", "Monaco", "MC", "MCO", "492"},
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
{"Montserrat", "Montserrat", "MS", "MSR", "500"},
{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
{"Oman", "Oman", "OM", "OMN", "512"},
{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
{"Nauru", "Nauru", "NR", "NRU", "520"},
{"Nepal", "Népal (le)", "NP", "NPL", "524"},
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
{"Curaçao", "Curaçao", "CW", "CUW", "531"},
{"Aruba", "Aruba", "AW", "ABW", "533"},
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
{"Niue", "Niue", "NU", "NIU", "570"},
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
{"Norway", "Norvège (la)", "NO", "NOR", "578"},
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
{"Palau", "Palaos (les)", "PW", "PLW", "585"},
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
{"Panama", "Panama (le)", "PA", "PAN", "591"},
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
{"Peru", "Pérou (le)", "PE", "PER", "604"},
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
{"Poland", "Pologne (la)", "PL", "POL", "616"},
{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
{"Réunion", "Réunion (La)", "RE", "REU", "638"},
{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
{"Anguilla", "Anguilla", "AI", "AIA", "660"},
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
{"Singapore", "Singapour", "SG", "SGP", "702"},
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
{"Spain", "Espagne (l')", "ES", "ESP", "724"},
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
{"Sweden", "Suède (la)", "SE", "SWE", "752"},
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
{"Togo", "Togo (le)", "TG", "TGO", "768"},
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
{"Tonga", "Tonga (les)", "TO", "TON", "776"},
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'exRépublique yougoslave de)", "MK", "MKD", "807"},
{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
{"Guernsey", "Guernesey", "GG", "GGY", "831"},
{"Jersey", "Jersey", "JE", "JEY", "832"},
{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
}
// ISO4217List is the list of ISO currency codes
var ISO4217List = []string{
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
"BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
"CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK",
"DJF", "DKK", "DOP", "DZD",
"EGP", "ERN", "ETB", "EUR",
"FJD", "FKP",
"GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
"HKD", "HNL", "HRK", "HTG", "HUF",
"IDR", "ILS", "INR", "IQD", "IRR", "ISK",
"JMD", "JOD", "JPY",
"KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
"LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
"MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN",
"NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
"OMR",
"PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
"QAR",
"RON", "RSD", "RUB", "RWF",
"SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "SVC", "SYP", "SZL",
"THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
"UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS",
"VEF", "VND", "VUV",
"WST",
"XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX",
"YER",
"ZAR", "ZMW", "ZWL",
}
// ISO693Entry stores ISO language codes
type ISO693Entry struct {
Alpha3bCode string
Alpha2Code string
English string
}
//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json
var ISO693List = []ISO693Entry{
{Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"},
{Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"},
{Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"},
{Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"},
{Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"},
{Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"},
{Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"},
{Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"},
{Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"},
{Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"},
{Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"},
{Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"},
{Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"},
{Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"},
{Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"},
{Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"},
{Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"},
{Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"},
{Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"},
{Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"},
{Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"},
{Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"},
{Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"},
{Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"},
{Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"},
{Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"},
{Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"},
{Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"},
{Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"},
{Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"},
{Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"},
{Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"},
{Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"},
{Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"},
{Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"},
{Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"},
{Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"},
{Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"},
{Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"},
{Alpha3bCode: "eng", Alpha2Code: "en", English: "English"},
{Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"},
{Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"},
{Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"},
{Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"},
{Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"},
{Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"},
{Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"},
{Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"},
{Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"},
{Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"},
{Alpha3bCode: "ger", Alpha2Code: "de", English: "German"},
{Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"},
{Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"},
{Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"},
{Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"},
{Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"},
{Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"},
{Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"},
{Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"},
{Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"},
{Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"},
{Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"},
{Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"},
{Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"},
{Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"},
{Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"},
{Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"},
{Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"},
{Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"},
{Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"},
{Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"},
{Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"},
{Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"},
{Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"},
{Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"},
{Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"},
{Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"},
{Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"},
{Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"},
{Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"},
{Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"},
{Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"},
{Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"},
{Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"},
{Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"},
{Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"},
{Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"},
{Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"},
{Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"},
{Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"},
{Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"},
{Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"},
{Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"},
{Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"},
{Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"},
{Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"},
{Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"},
{Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"},
{Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"},
{Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"},
{Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"},
{Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"},
{Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"},
{Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"},
{Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"},
{Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"},
{Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"},
{Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"},
{Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"},
{Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"},
{Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"},
{Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"},
{Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"},
{Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"},
{Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"},
{Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"},
{Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"},
{Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"},
{Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"},
{Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"},
{Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"},
{Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"},
{Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"},
{Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"},
{Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"},
{Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"},
{Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"},
{Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"},
{Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"},
{Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"},
{Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"},
{Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"},
{Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"},
{Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"},
{Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"},
{Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"},
{Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"},
{Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"},
{Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"},
{Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"},
{Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"},
{Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"},
{Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"},
{Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"},
{Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"},
{Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"},
{Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"},
{Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"},
{Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"},
{Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"},
{Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"},
{Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"},
{Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"},
{Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"},
{Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"},
{Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"},
{Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"},
{Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"},
{Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"},
{Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"},
{Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"},
{Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"},
{Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"},
{Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"},
{Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"},
{Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"},
{Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"},
{Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"},
{Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"},
{Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"},
{Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"},
{Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"},
{Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"},
{Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"},
{Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"},
{Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"},
{Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"},
{Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"},
{Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"},
{Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"},
{Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"},
{Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"},
{Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"},
{Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"},
}

262
vendor/github.com/asaskevich/govalidator/utils.go generated vendored Normal file
View File

@@ -0,0 +1,262 @@
package govalidator
import (
"errors"
"fmt"
"html"
"math"
"path"
"regexp"
"strings"
"unicode"
"unicode/utf8"
)
// Contains check if the string contains the substring.
func Contains(str, substring string) bool {
return strings.Contains(str, substring)
}
// Matches check if string matches the pattern (pattern is regular expression)
// In case of error return false
func Matches(str, pattern string) bool {
match, _ := regexp.MatchString(pattern, str)
return match
}
// LeftTrim trim characters from the left-side of the input.
// If second argument is empty, it's will be remove leading spaces.
func LeftTrim(str, chars string) string {
if chars == "" {
return strings.TrimLeftFunc(str, unicode.IsSpace)
}
r, _ := regexp.Compile("^[" + chars + "]+")
return r.ReplaceAllString(str, "")
}
// RightTrim trim characters from the right-side of the input.
// If second argument is empty, it's will be remove spaces.
func RightTrim(str, chars string) string {
if chars == "" {
return strings.TrimRightFunc(str, unicode.IsSpace)
}
r, _ := regexp.Compile("[" + chars + "]+$")
return r.ReplaceAllString(str, "")
}
// Trim trim characters from both sides of the input.
// If second argument is empty, it's will be remove spaces.
func Trim(str, chars string) string {
return LeftTrim(RightTrim(str, chars), chars)
}
// WhiteList remove characters that do not appear in the whitelist.
func WhiteList(str, chars string) string {
pattern := "[^" + chars + "]+"
r, _ := regexp.Compile(pattern)
return r.ReplaceAllString(str, "")
}
// BlackList remove characters that appear in the blacklist.
func BlackList(str, chars string) string {
pattern := "[" + chars + "]+"
r, _ := regexp.Compile(pattern)
return r.ReplaceAllString(str, "")
}
// StripLow remove characters with a numerical value < 32 and 127, mostly control characters.
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
func StripLow(str string, keepNewLines bool) string {
chars := ""
if keepNewLines {
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
} else {
chars = "\x00-\x1F\x7F"
}
return BlackList(str, chars)
}
// ReplacePattern replace regular expression pattern in string
func ReplacePattern(str, pattern, replace string) string {
r, _ := regexp.Compile(pattern)
return r.ReplaceAllString(str, replace)
}
// Escape replace <, >, & and " with HTML entities.
var Escape = html.EscapeString
func addSegment(inrune, segment []rune) []rune {
if len(segment) == 0 {
return inrune
}
if len(inrune) != 0 {
inrune = append(inrune, '_')
}
inrune = append(inrune, segment...)
return inrune
}
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
// Ex.: my_func => MyFunc
func UnderscoreToCamelCase(s string) string {
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
}
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
// Ex.: MyFunc => my_func
func CamelCaseToUnderscore(str string) string {
var output []rune
var segment []rune
for _, r := range str {
if !unicode.IsLower(r) && string(r) != "_" {
output = addSegment(output, segment)
segment = nil
}
segment = append(segment, unicode.ToLower(r))
}
output = addSegment(output, segment)
return string(output)
}
// Reverse return reversed string
func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
// GetLines split string by "\n" and return array of lines
func GetLines(s string) []string {
return strings.Split(s, "\n")
}
// GetLine return specified line of multiline string
func GetLine(s string, index int) (string, error) {
lines := GetLines(s)
if index < 0 || index >= len(lines) {
return "", errors.New("line index out of bounds")
}
return lines[index], nil
}
// RemoveTags remove all tags from HTML string
func RemoveTags(s string) string {
return ReplacePattern(s, "<[^>]*>", "")
}
// SafeFileName return safe string that can be used in file names
func SafeFileName(str string) string {
name := strings.ToLower(str)
name = path.Clean(path.Base(name))
name = strings.Trim(name, " ")
separators, err := regexp.Compile(`[ &_=+:]`)
if err == nil {
name = separators.ReplaceAllString(name, "-")
}
legal, err := regexp.Compile(`[^[:alnum:]-.]`)
if err == nil {
name = legal.ReplaceAllString(name, "")
}
for strings.Contains(name, "--") {
name = strings.Replace(name, "--", "-", -1)
}
return name
}
// NormalizeEmail canonicalize an email address.
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
// normalized to @gmail.com.
func NormalizeEmail(str string) (string, error) {
if !IsEmail(str) {
return "", fmt.Errorf("%s is not an email", str)
}
parts := strings.Split(str, "@")
parts[0] = strings.ToLower(parts[0])
parts[1] = strings.ToLower(parts[1])
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
parts[1] = "gmail.com"
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
}
return strings.Join(parts, "@"), nil
}
// Truncate a string to the closest length without breaking words.
func Truncate(str string, length int, ending string) string {
var aftstr, befstr string
if len(str) > length {
words := strings.Fields(str)
before, present := 0, 0
for i := range words {
befstr = aftstr
before = present
aftstr = aftstr + words[i] + " "
present = len(aftstr)
if present > length && i != 0 {
if (length - before) < (present - length) {
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
}
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
}
}
}
return str
}
// PadLeft pad left side of string if size of string is less then indicated pad length
func PadLeft(str string, padStr string, padLen int) string {
return buildPadStr(str, padStr, padLen, true, false)
}
// PadRight pad right side of string if size of string is less then indicated pad length
func PadRight(str string, padStr string, padLen int) string {
return buildPadStr(str, padStr, padLen, false, true)
}
// PadBoth pad sides of string if size of string is less then indicated pad length
func PadBoth(str string, padStr string, padLen int) string {
return buildPadStr(str, padStr, padLen, true, true)
}
// PadString either left, right or both sides, not the padding string can be unicode and more then one
// character
func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string {
// When padded length is less then the current string size
if padLen < utf8.RuneCountInString(str) {
return str
}
padLen -= utf8.RuneCountInString(str)
targetLen := padLen
targetLenLeft := targetLen
targetLenRight := targetLen
if padLeft && padRight {
targetLenLeft = padLen / 2
targetLenRight = padLen - targetLenLeft
}
strToRepeatLen := utf8.RuneCountInString(padStr)
repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen)))
repeatedString := strings.Repeat(padStr, repeatTimes)
leftSide := ""
if padLeft {
leftSide = repeatedString[0:targetLenLeft]
}
rightSide := ""
if padRight {
rightSide = repeatedString[0:targetLenRight]
}
return leftSide + str + rightSide
}

501
vendor/github.com/asaskevich/govalidator/utils_test.go generated vendored Normal file
View File

@@ -0,0 +1,501 @@
package govalidator
import (
"reflect"
"testing"
)
func TestContains(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
expected bool
}{
{"abacada", "", true},
{"abacada", "ritir", false},
{"abacada", "a", true},
{"abacada", "aca", true},
}
for _, test := range tests {
actual := Contains(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected Contains(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
func TestMatches(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
expected bool
}{
{"123456789", "[0-9]+", true},
{"abacada", "cab$", false},
{"111222333", "((111|222|333)+)+", true},
{"abacaba", "((123+]", false},
}
for _, test := range tests {
actual := Matches(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected Matches(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
func TestLeftTrim(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
expected string
}{
{" \r\n\tfoo \r\n\t ", "", "foo \r\n\t "},
{"010100201000", "01", "201000"},
}
for _, test := range tests {
actual := LeftTrim(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected LeftTrim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
func TestRightTrim(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
expected string
}{
{" \r\n\tfoo \r\n\t ", "", " \r\n\tfoo"},
{"010100201000", "01", "0101002"},
}
for _, test := range tests {
actual := RightTrim(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected RightTrim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
func TestTrim(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
expected string
}{
{" \r\n\tfoo \r\n\t ", "", "foo"},
{"010100201000", "01", "2"},
{"1234567890987654321", "1-8", "909"},
}
for _, test := range tests {
actual := Trim(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected Trim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
// This small example illustrate how to work with Trim function.
func ExampleTrim() {
// Remove from left and right spaces and "\r", "\n", "\t" characters
println(Trim(" \r\r\ntext\r \t\n", "") == "text")
// Remove from left and right characters that are between "1" and "8".
// "1-8" is like full list "12345678".
println(Trim("1234567890987654321", "1-8") == "909")
}
func TestWhiteList(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
expected string
}{
{"abcdef", "abc", "abc"},
{"aaaaaaaaaabbbbbbbbbb", "abc", "aaaaaaaaaabbbbbbbbbb"},
{"a1b2c3", "abc", "abc"},
{" ", "abc", ""},
{"a3a43a5a4a3a2a23a4a5a4a3a4", "a-z", "aaaaaaaaaaaa"},
}
for _, test := range tests {
actual := WhiteList(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected WhiteList(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
// This small example illustrate how to work with WhiteList function.
func ExampleWhiteList() {
// Remove all characters from string ignoring characters between "a" and "z"
println(WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
}
func TestBlackList(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
expected string
}{
{"abcdef", "abc", "def"},
{"aaaaaaaaaabbbbbbbbbb", "abc", ""},
{"a1b2c3", "abc", "123"},
{" ", "abc", " "},
{"a3a43a5a4a3a2a23a4a5a4a3a4", "a-z", "34354322345434"},
}
for _, test := range tests {
actual := BlackList(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected BlackList(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
func TestStripLow(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 bool
expected string
}{
{"foo\x00", false, "foo"},
{"\x7Ffoo\x02", false, "foo"},
{"\x01\x09", false, ""},
{"foo\x0A\x0D", false, "foo"},
{"perch\u00e9", false, "perch\u00e9"},
{"\u20ac", false, "\u20ac"},
{"\u2206\x0A", false, "\u2206"},
{"foo\x0A\x0D", true, "foo\x0A\x0D"},
{"\x03foo\x0A\x0D", true, "foo\x0A\x0D"},
}
for _, test := range tests {
actual := StripLow(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected StripLow(%q,%t) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
func TestReplacePattern(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
param3 string
expected string
}{
{"ab123ba", "[0-9]+", "aca", "abacaba"},
{"abacaba", "[0-9]+", "aca", "abacaba"},
{"httpftp://github.comio", "(ftp|io)", "", "http://github.com"},
{"aaaaaaaaaa", "a", "", ""},
{"http123123ftp://git534543hub.comio", "(ftp|io|[0-9]+)", "", "http://github.com"},
}
for _, test := range tests {
actual := ReplacePattern(test.param1, test.param2, test.param3)
if actual != test.expected {
t.Errorf("Expected ReplacePattern(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
}
}
}
// This small example illustrate how to work with ReplacePattern function.
func ExampleReplacePattern() {
// Replace in "http123123ftp://git534543hub.comio" following (pattern "(ftp|io|[0-9]+)"):
// - Sequence "ftp".
// - Sequence "io".
// - Sequence of digits.
// with empty string.
println(ReplacePattern("http123123ftp://git534543hub.comio", "(ftp|io|[0-9]+)", "") == "http://github.com")
}
func TestEscape(t *testing.T) {
t.Parallel()
var tests = []struct {
param string
expected string
}{
{`<img alt="foo&bar">`, "&lt;img alt=&#34;foo&amp;bar&#34;&gt;"},
}
for _, test := range tests {
actual := Escape(test.param)
if actual != test.expected {
t.Errorf("Expected Escape(%q) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestUnderscoreToCamelCase(t *testing.T) {
t.Parallel()
var tests = []struct {
param string
expected string
}{
{"a_b_c", "ABC"},
{"my_func", "MyFunc"},
{"1ab_cd", "1abCd"},
}
for _, test := range tests {
actual := UnderscoreToCamelCase(test.param)
if actual != test.expected {
t.Errorf("Expected UnderscoreToCamelCase(%q) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestCamelCaseToUnderscore(t *testing.T) {
t.Parallel()
var tests = []struct {
param string
expected string
}{
{"MyFunc", "my_func"},
{"ABC", "a_b_c"},
{"1B", "1_b"},
{"foo_bar", "foo_bar"},
}
for _, test := range tests {
actual := CamelCaseToUnderscore(test.param)
if actual != test.expected {
t.Errorf("Expected CamelCaseToUnderscore(%q) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestReverse(t *testing.T) {
t.Parallel()
var tests = []struct {
param string
expected string
}{
{"abc", "cba"},
{"カタカナ", "ナカタカ"},
}
for _, test := range tests {
actual := Reverse(test.param)
if actual != test.expected {
t.Errorf("Expected Reverse(%q) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestGetLines(t *testing.T) {
t.Parallel()
var tests = []struct {
param string
expected []string
}{
{"abc", []string{"abc"}},
{"a\nb\nc", []string{"a", "b", "c"}},
}
for _, test := range tests {
actual := GetLines(test.param)
if !reflect.DeepEqual(actual, test.expected) {
t.Errorf("Expected GetLines(%q) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestGetLine(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 int
expected string
}{
{"abc", 0, "abc"},
{"a\nb\nc", 0, "a"},
{"abc", -1, ""},
{"abacaba\n", 1, ""},
{"abc", 3, ""},
}
for _, test := range tests {
actual, _ := GetLine(test.param1, test.param2)
if actual != test.expected {
t.Errorf("Expected GetLine(%q, %d) to be %v, got %v", test.param1, test.param2, test.expected, actual)
}
}
}
func TestRemoveTags(t *testing.T) {
t.Parallel()
var tests = []struct {
param string
expected string
}{
{"abc", "abc"},
{"<!-- Test -->", ""},
{"<div><div><p><a>Text</a></p></div></div>", "Text"},
{`<a href="#">Link</a>`, "Link"},
}
for _, test := range tests {
actual := RemoveTags(test.param)
if actual != test.expected {
t.Errorf("Expected RemoveTags(%q) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestSafeFileName(t *testing.T) {
t.Parallel()
var tests = []struct {
param string
expected string
}{
{"abc", "abc"},
{"123456789 '_-?ASDF@£$%£%^é.html", "123456789-asdf.html"},
{"ReadMe.md", "readme.md"},
{"file:///c:/test.go", "test.go"},
{"../../../Hello World!.txt", "hello-world.txt"},
}
for _, test := range tests {
actual := SafeFileName(test.param)
if actual != test.expected {
t.Errorf("Expected SafeFileName(%q) to be %v, got %v", test.param, test.expected, actual)
}
}
}
func TestNormalizeEmail(t *testing.T) {
t.Parallel()
var tests = []struct {
param string
expected string
}{
{`test@me.com`, `test@me.com`},
{`some.name@gmail.com`, `somename@gmail.com`},
{`some.name@googlemail.com`, `somename@gmail.com`},
{`some.name+extension@gmail.com`, `somename@gmail.com`},
{`some.name+extension@googlemail.com`, `somename@gmail.com`},
{`some.name.middlename+extension@gmail.com`, `somenamemiddlename@gmail.com`},
{`some.name.middlename+extension@googlemail.com`, `somenamemiddlename@gmail.com`},
{`some.name.midd.lena.me.+extension@gmail.com`, `somenamemiddlename@gmail.com`},
{`some.name.midd.lena.me.+extension@googlemail.com`, `somenamemiddlename@gmail.com`},
{`some.name+extension@unknown.com`, `some.name+extension@unknown.com`},
{`hans@m端ller.com`, `hans@m端ller.com`},
{`hans`, ``},
}
for _, test := range tests {
actual, err := NormalizeEmail(test.param)
if actual != test.expected {
t.Errorf("Expected NormalizeEmail(%q) to be %v, got %v, err %v", test.param, test.expected, actual, err)
}
}
}
func TestTruncate(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 int
param3 string
expected string
}{
{`Lorem ipsum dolor sit amet, consectetur adipiscing elit.`, 25, `...`, `Lorem ipsum dolor sit amet...`},
{`Measuring programming progress by lines of code is like measuring aircraft building progress by weight.`, 35, ` new born babies!`, `Measuring programming progress by new born babies!`},
{`Testestestestestestestestestest testestestestestestestestest`, 7, `...`, `Testestestestestestestestestest...`},
{`Testing`, 7, `...`, `Testing`},
}
for _, test := range tests {
actual := Truncate(test.param1, test.param2, test.param3)
if actual != test.expected {
t.Errorf("Expected Truncate(%q, %d, %q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
}
}
}
func TestPadLeft(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
param3 int
expected string
}{
{"こんにちは", "xyz", 12, "xyzxyzxこんにちは"},
{"こんにちは", "xyz", 11, "xyzxyzこんにちは"},
{"abc", "x", 5, "xxabc"},
{"abc", "xyz", 5, "xyabc"},
{"abcde", "xyz", 5, "abcde"},
{"abcde", "xyz", 4, "abcde"},
}
for _, test := range tests {
actual := PadLeft(test.param1, test.param2, test.param3)
if actual != test.expected {
t.Errorf("Expected PadLeft(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
}
}
}
func TestPadRight(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
param3 int
expected string
}{
{"こんにちは", "xyz", 12, "こんにちはxyzxyzx"},
{"こんにちは", "xyz", 11, "こんにちはxyzxyz"},
{"abc", "x", 5, "abcxx"},
{"abc", "xyz", 5, "abcxy"},
{"abcde", "xyz", 5, "abcde"},
{"abcde", "xyz", 4, "abcde"},
}
for _, test := range tests {
actual := PadRight(test.param1, test.param2, test.param3)
if actual != test.expected {
t.Errorf("Expected PadRight(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
}
}
}
func TestPadBoth(t *testing.T) {
t.Parallel()
var tests = []struct {
param1 string
param2 string
param3 int
expected string
}{
{"こんにちは", "xyz", 12, "xyzこんにちはxyzx"},
{"こんにちは", "xyz", 11, "xyzこんにちはxyz"},
{"abc", "x", 5, "xabcx"},
{"abc", "xyz", 5, "xabcx"},
{"abcde", "xyz", 5, "abcde"},
{"abcde", "xyz", 4, "abcde"},
}
for _, test := range tests {
actual := PadBoth(test.param1, test.param2, test.param3)
if actual != test.expected {
t.Errorf("Expected PadBoth(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
}
}
}

1167
vendor/github.com/asaskevich/govalidator/validator.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

15
vendor/github.com/asaskevich/govalidator/wercker.yml generated vendored Normal file
View File

@@ -0,0 +1,15 @@
box: golang
build:
steps:
- setup-go-workspace
- script:
name: go get
code: |
go version
go get -t ./...
- script:
name: go test
code: |
go test -race ./...

2
vendor/github.com/boj/redistore/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
.DS_Store

19
vendor/github.com/boj/redistore/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2013 Brian Jones
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.

52
vendor/github.com/boj/redistore/README.md generated vendored Normal file
View File

@@ -0,0 +1,52 @@
# redistore
[![Build Status](https://drone.io/github.com/boj/redistore/status.png)](https://drone.io/github.com/boj/redistore/latest)
A session store backend for [gorilla/sessions](http://www.gorillatoolkit.org/pkg/sessions) - [src](https://github.com/gorilla/sessions).
## Requirements
Depends on the [Redigo](https://github.com/garyburd/redigo) Redis library.
## Installation
go get gopkg.in/boj/redistore.v1
## Documentation
Available on [godoc.org](http://www.godoc.org/gopkg.in/boj/redistore.v1).
See http://www.gorillatoolkit.org/pkg/sessions for full documentation on underlying interface.
### Example
// Fetch new store.
store, err := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
if err != nil {
panic(err)
}
defer store.Close()
// Get a session.
session, err = store.Get(req, "session-key")
if err != nil {
log.Error(err.Error())
}
// Add a value.
session.Values["foo"] = "bar"
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
// Delete session.
session.Options.MaxAge = -1
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
// Change session storage configuration for MaxAge = 10 days.
store.SetMaxAge(10*24*3600)

4
vendor/github.com/boj/redistore/doc.go generated vendored Normal file
View File

@@ -0,0 +1,4 @@
/*
Package redistore is a session store backend for gorilla/sessions
*/
package redistore

358
vendor/github.com/boj/redistore/redistore.go generated vendored Normal file
View File

@@ -0,0 +1,358 @@
// Copyright 2012 Brian "bojo" Jones. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package redistore
import (
"bytes"
"encoding/base32"
"encoding/gob"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/garyburd/redigo/redis"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
)
// Amount of time for cookies/redis keys to expire.
var sessionExpire = 86400 * 30
// SessionSerializer provides an interface hook for alternative serializers
type SessionSerializer interface {
Deserialize(d []byte, ss *sessions.Session) error
Serialize(ss *sessions.Session) ([]byte, error)
}
// JSONSerializer encode the session map to JSON.
type JSONSerializer struct{}
// Serialize to JSON. Will err if there are unmarshalable key values
func (s JSONSerializer) Serialize(ss *sessions.Session) ([]byte, error) {
m := make(map[string]interface{}, len(ss.Values))
for k, v := range ss.Values {
ks, ok := k.(string)
if !ok {
err := fmt.Errorf("Non-string key value, cannot serialize session to JSON: %v", k)
fmt.Printf("redistore.JSONSerializer.serialize() Error: %v", err)
return nil, err
}
m[ks] = v
}
return json.Marshal(m)
}
// Deserialize back to map[string]interface{}
func (s JSONSerializer) Deserialize(d []byte, ss *sessions.Session) error {
m := make(map[string]interface{})
err := json.Unmarshal(d, &m)
if err != nil {
fmt.Printf("redistore.JSONSerializer.deserialize() Error: %v", err)
return err
}
for k, v := range m {
ss.Values[k] = v
}
return nil
}
// GobSerializer uses gob package to encode the session map
type GobSerializer struct{}
// Serialize using gob
func (s GobSerializer) Serialize(ss *sessions.Session) ([]byte, error) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(ss.Values)
if err == nil {
return buf.Bytes(), nil
}
return nil, err
}
// Deserialize back to map[interface{}]interface{}
func (s GobSerializer) Deserialize(d []byte, ss *sessions.Session) error {
dec := gob.NewDecoder(bytes.NewBuffer(d))
return dec.Decode(&ss.Values)
}
// RediStore stores sessions in a redis backend.
type RediStore struct {
Pool *redis.Pool
Codecs []securecookie.Codec
Options *sessions.Options // default configuration
DefaultMaxAge int // default Redis TTL for a MaxAge == 0 session
maxLength int
keyPrefix string
serializer SessionSerializer
}
// SetMaxLength sets RediStore.maxLength if the `l` argument is greater or equal 0
// maxLength restricts the maximum length of new sessions to l.
// If l is 0 there is no limit to the size of a session, use with caution.
// The default for a new RediStore is 4096. Redis allows for max.
// value sizes of up to 512MB (http://redis.io/topics/data-types)
// Default: 4096,
func (s *RediStore) SetMaxLength(l int) {
if l >= 0 {
s.maxLength = l
}
}
// SetKeyPrefix set the prefix
func (s *RediStore) SetKeyPrefix(p string) {
s.keyPrefix = p
}
// SetSerializer sets the serializer
func (s *RediStore) SetSerializer(ss SessionSerializer) {
s.serializer = ss
}
// SetMaxAge restricts the maximum age, in seconds, of the session record
// both in database and a browser. This is to change session storage configuration.
// If you want just to remove session use your session `s` object and change it's
// `Options.MaxAge` to -1, as specified in
// http://godoc.org/github.com/gorilla/sessions#Options
//
// Default is the one provided by this package value - `sessionExpire`.
// Set it to 0 for no restriction.
// Because we use `MaxAge` also in SecureCookie crypting algorithm you should
// use this function to change `MaxAge` value.
func (s *RediStore) SetMaxAge(v int) {
var c *securecookie.SecureCookie
var ok bool
s.Options.MaxAge = v
for i := range s.Codecs {
if c, ok = s.Codecs[i].(*securecookie.SecureCookie); ok {
c.MaxAge(v)
} else {
fmt.Printf("Can't change MaxAge on codec %v\n", s.Codecs[i])
}
}
}
func dial(network, address, password string) (redis.Conn, error) {
c, err := redis.Dial(network, address)
if err != nil {
return nil, err
}
if password != "" {
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
return nil, err
}
}
return c, err
}
// NewRediStore returns a new RediStore.
// size: maximum number of idle connections.
func NewRediStore(size int, network, address, password string, keyPairs ...[]byte) (*RediStore, error) {
return NewRediStoreWithPool(&redis.Pool{
MaxIdle: size,
IdleTimeout: 240 * time.Second,
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
Dial: func() (redis.Conn, error) {
return dial(network, address, password)
},
}, keyPairs...)
}
func dialWithDB(network, address, password, DB string) (redis.Conn, error) {
c, err := dial(network, address, password)
if err != nil {
return nil, err
}
if _, err := c.Do("SELECT", DB); err != nil {
c.Close()
return nil, err
}
return c, err
}
// NewRediStoreWithDB - like NewRedisStore but accepts `DB` parameter to select
// redis DB instead of using the default one ("0")
func NewRediStoreWithDB(size int, network, address, password, DB string, keyPairs ...[]byte) (*RediStore, error) {
return NewRediStoreWithPool(&redis.Pool{
MaxIdle: size,
IdleTimeout: 240 * time.Second,
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
Dial: func() (redis.Conn, error) {
return dialWithDB(network, address, password, DB)
},
}, keyPairs...)
}
// NewRediStoreWithPool instantiates a RediStore with a *redis.Pool passed in.
func NewRediStoreWithPool(pool *redis.Pool, keyPairs ...[]byte) (*RediStore, error) {
rs := &RediStore{
// http://godoc.org/github.com/garyburd/redigo/redis#Pool
Pool: pool,
Codecs: securecookie.CodecsFromPairs(keyPairs...),
Options: &sessions.Options{
Path: "/",
MaxAge: sessionExpire,
},
DefaultMaxAge: 60 * 20, // 20 minutes seems like a reasonable default
maxLength: 4096,
keyPrefix: "session_",
serializer: GobSerializer{},
}
_, err := rs.ping()
return rs, err
}
// Close closes the underlying *redis.Pool
func (s *RediStore) Close() error {
return s.Pool.Close()
}
// Get returns a session for the given name after adding it to the registry.
//
// See gorilla/sessions FilesystemStore.Get().
func (s *RediStore) Get(r *http.Request, name string) (*sessions.Session, error) {
return sessions.GetRegistry(r).Get(s, name)
}
// New returns a session for the given name without adding it to the registry.
//
// See gorilla/sessions FilesystemStore.New().
func (s *RediStore) New(r *http.Request, name string) (*sessions.Session, error) {
var err error
session := sessions.NewSession(s, name)
// make a copy
options := *s.Options
session.Options = &options
session.IsNew = true
if c, errCookie := r.Cookie(name); errCookie == nil {
err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
if err == nil {
ok, err := s.load(session)
session.IsNew = !(err == nil && ok) // not new if no error and data available
}
}
return session, err
}
// Save adds a single session to the response.
func (s *RediStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
// Marked for deletion.
if session.Options.MaxAge < 0 {
if err := s.delete(session); err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
} else {
// Build an alphanumeric key for the redis store.
if session.ID == "" {
session.ID = strings.TrimRight(base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)), "=")
}
if err := s.save(session); err != nil {
return err
}
encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, s.Codecs...)
if err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), encoded, session.Options))
}
return nil
}
// Delete removes the session from redis, and sets the cookie to expire.
//
// WARNING: This method should be considered deprecated since it is not exposed via the gorilla/sessions interface.
// Set session.Options.MaxAge = -1 and call Save instead. - July 18th, 2013
func (s *RediStore) Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
conn := s.Pool.Get()
defer conn.Close()
if _, err := conn.Do("DEL", s.keyPrefix+session.ID); err != nil {
return err
}
// Set cookie to expire.
options := *session.Options
options.MaxAge = -1
http.SetCookie(w, sessions.NewCookie(session.Name(), "", &options))
// Clear session values.
for k := range session.Values {
delete(session.Values, k)
}
return nil
}
// ping does an internal ping against a server to check if it is alive.
func (s *RediStore) ping() (bool, error) {
conn := s.Pool.Get()
defer conn.Close()
data, err := conn.Do("PING")
if err != nil || data == nil {
return false, err
}
return (data == "PONG"), nil
}
// save stores the session in redis.
func (s *RediStore) save(session *sessions.Session) error {
b, err := s.serializer.Serialize(session)
if err != nil {
return err
}
if s.maxLength != 0 && len(b) > s.maxLength {
return errors.New("SessionStore: the value to store is too big")
}
conn := s.Pool.Get()
defer conn.Close()
if err = conn.Err(); err != nil {
return err
}
age := session.Options.MaxAge
if age == 0 {
age = s.DefaultMaxAge
}
_, err = conn.Do("SETEX", s.keyPrefix+session.ID, age, b)
return err
}
// load reads the session from redis.
// returns true if there is a sessoin data in DB
func (s *RediStore) load(session *sessions.Session) (bool, error) {
conn := s.Pool.Get()
defer conn.Close()
if err := conn.Err(); err != nil {
return false, err
}
data, err := conn.Do("GET", s.keyPrefix+session.ID)
if err != nil {
return false, err
}
if data == nil {
return false, nil // no data was associated with this key
}
b, err := redis.Bytes(data, err)
if err != nil {
return false, err
}
return true, s.serializer.Deserialize(b, session)
}
// delete removes keys from redis if MaxAge<0
func (s *RediStore) delete(session *sessions.Session) error {
conn := s.Pool.Get()
defer conn.Close()
if _, err := conn.Do("DEL", s.keyPrefix+session.ID); err != nil {
return err
}
return nil
}

404
vendor/github.com/boj/redistore/redistore_test.go generated vendored Normal file
View File

@@ -0,0 +1,404 @@
package redistore
import (
"bytes"
"encoding/base64"
"encoding/gob"
"net/http"
"net/http/httptest"
"testing"
"github.com/gorilla/sessions"
)
// ----------------------------------------------------------------------------
// ResponseRecorder
// ----------------------------------------------------------------------------
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// ResponseRecorder is an implementation of http.ResponseWriter that
// records its mutations for later inspection in tests.
type ResponseRecorder struct {
Code int // the HTTP response code from WriteHeader
HeaderMap http.Header // the HTTP response headers
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
Flushed bool
}
// NewRecorder returns an initialized ResponseRecorder.
func NewRecorder() *ResponseRecorder {
return &ResponseRecorder{
HeaderMap: make(http.Header),
Body: new(bytes.Buffer),
}
}
// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
const DefaultRemoteAddr = "1.2.3.4"
// Header returns the response headers.
func (rw *ResponseRecorder) Header() http.Header {
return rw.HeaderMap
}
// Write always succeeds and writes to rw.Body, if not nil.
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
if rw.Body != nil {
rw.Body.Write(buf)
}
if rw.Code == 0 {
rw.Code = http.StatusOK
}
return len(buf), nil
}
// WriteHeader sets rw.Code.
func (rw *ResponseRecorder) WriteHeader(code int) {
rw.Code = code
}
// Flush sets rw.Flushed to true.
func (rw *ResponseRecorder) Flush() {
rw.Flushed = true
}
// ----------------------------------------------------------------------------
type FlashMessage struct {
Type int
Message string
}
func TestRediStore(t *testing.T) {
var req *http.Request
var rsp *ResponseRecorder
var hdr http.Header
var err error
var ok bool
var cookies []string
var session *sessions.Session
var flashes []interface{}
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Round 1 ----------------------------------------------------------------
// RedisStore
store, err := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
if err != nil {
t.Fatal(err.Error())
}
defer store.Close()
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
rsp = NewRecorder()
// Get a session.
if session, err = store.Get(req, "session-key"); err != nil {
t.Fatalf("Error getting session: %v", err)
}
// Get a flash.
flashes = session.Flashes()
if len(flashes) != 0 {
t.Errorf("Expected empty flashes; Got %v", flashes)
}
// Add some flashes.
session.AddFlash("foo")
session.AddFlash("bar")
// Custom key.
session.AddFlash("baz", "custom_key")
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
hdr = rsp.Header()
cookies, ok = hdr["Set-Cookie"]
if !ok || len(cookies) != 1 {
t.Fatalf("No cookies. Header:", hdr)
}
// Round 2 ----------------------------------------------------------------
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
req.Header.Add("Cookie", cookies[0])
rsp = NewRecorder()
// Get a session.
if session, err = store.Get(req, "session-key"); err != nil {
t.Fatalf("Error getting session: %v", err)
}
// Check all saved values.
flashes = session.Flashes()
if len(flashes) != 2 {
t.Fatalf("Expected flashes; Got %v", flashes)
}
if flashes[0] != "foo" || flashes[1] != "bar" {
t.Errorf("Expected foo,bar; Got %v", flashes)
}
flashes = session.Flashes()
if len(flashes) != 0 {
t.Errorf("Expected dumped flashes; Got %v", flashes)
}
// Custom key.
flashes = session.Flashes("custom_key")
if len(flashes) != 1 {
t.Errorf("Expected flashes; Got %v", flashes)
} else if flashes[0] != "baz" {
t.Errorf("Expected baz; Got %v", flashes)
}
flashes = session.Flashes("custom_key")
if len(flashes) != 0 {
t.Errorf("Expected dumped flashes; Got %v", flashes)
}
// RediStore specific
// Set MaxAge to -1 to mark for deletion.
session.Options.MaxAge = -1
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
// Round 3 ----------------------------------------------------------------
// Custom type
// RedisStore
store, err = NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
if err != nil {
t.Fatal(err.Error())
}
defer store.Close()
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
rsp = NewRecorder()
// Get a session.
if session, err = store.Get(req, "session-key"); err != nil {
t.Fatalf("Error getting session: %v", err)
}
// Get a flash.
flashes = session.Flashes()
if len(flashes) != 0 {
t.Errorf("Expected empty flashes; Got %v", flashes)
}
// Add some flashes.
session.AddFlash(&FlashMessage{42, "foo"})
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
hdr = rsp.Header()
cookies, ok = hdr["Set-Cookie"]
if !ok || len(cookies) != 1 {
t.Fatalf("No cookies. Header:", hdr)
}
// Round 4 ----------------------------------------------------------------
// Custom type
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
req.Header.Add("Cookie", cookies[0])
rsp = NewRecorder()
// Get a session.
if session, err = store.Get(req, "session-key"); err != nil {
t.Fatalf("Error getting session: %v", err)
}
// Check all saved values.
flashes = session.Flashes()
if len(flashes) != 1 {
t.Fatalf("Expected flashes; Got %v", flashes)
}
custom := flashes[0].(FlashMessage)
if custom.Type != 42 || custom.Message != "foo" {
t.Errorf("Expected %#v, got %#v", FlashMessage{42, "foo"}, custom)
}
// RediStore specific
// Set MaxAge to -1 to mark for deletion.
session.Options.MaxAge = -1
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
// Round 5 ----------------------------------------------------------------
// RediStore Delete session (deprecated)
//req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
//req.Header.Add("Cookie", cookies[0])
//rsp = NewRecorder()
//// Get a session.
//if session, err = store.Get(req, "session-key"); err != nil {
// t.Fatalf("Error getting session: %v", err)
//}
//// Delete session.
//if err = store.Delete(req, rsp, session); err != nil {
// t.Fatalf("Error deleting session: %v", err)
//}
//// Get a flash.
//flashes = session.Flashes()
//if len(flashes) != 0 {
// t.Errorf("Expected empty flashes; Got %v", flashes)
//}
//hdr = rsp.Header()
//cookies, ok = hdr["Set-Cookie"]
//if !ok || len(cookies) != 1 {
// t.Fatalf("No cookies. Header:", hdr)
//}
// Round 6 ----------------------------------------------------------------
// RediStore change MaxLength of session
store, err = NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
if err != nil {
t.Fatal(err.Error())
}
req, err = http.NewRequest("GET", "http://www.example.com", nil)
if err != nil {
t.Fatal("failed to create request", err)
}
w := httptest.NewRecorder()
session, err = store.New(req, "my session")
session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2))
err = session.Save(req, w)
if err == nil {
t.Fatal("expected an error, got nil")
}
store.SetMaxLength(4096 * 3) // A bit more than the value size to account for encoding overhead.
err = session.Save(req, w)
if err != nil {
t.Fatal("failed to Save:", err)
}
// Round 7 ----------------------------------------------------------------
// RedisStoreWithDB
store, err = NewRediStoreWithDB(10, "tcp", ":6379", "", "1", []byte("secret-key"))
if err != nil {
t.Fatal(err.Error())
}
defer store.Close()
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
rsp = NewRecorder()
// Get a session. Using the same key as previously, but on different DB
if session, err = store.Get(req, "session-key"); err != nil {
t.Fatalf("Error getting session: %v", err)
}
// Get a flash.
flashes = session.Flashes()
if len(flashes) != 0 {
t.Errorf("Expected empty flashes; Got %v", flashes)
}
// Add some flashes.
session.AddFlash("foo")
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
hdr = rsp.Header()
cookies, ok = hdr["Set-Cookie"]
if !ok || len(cookies) != 1 {
t.Fatalf("No cookies. Header:", hdr)
}
// Get a session.
req.Header.Add("Cookie", cookies[0])
if session, err = store.Get(req, "session-key"); err != nil {
t.Fatalf("Error getting session: %v", err)
}
// Check all saved values.
flashes = session.Flashes()
if len(flashes) != 1 {
t.Fatalf("Expected flashes; Got %v", flashes)
}
if flashes[0] != "foo" {
t.Errorf("Expected foo,bar; Got %v", flashes)
}
// Round 8 ----------------------------------------------------------------
// JSONSerializer
// RedisStore
store, err = NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
store.SetSerializer(JSONSerializer{})
if err != nil {
t.Fatal(err.Error())
}
defer store.Close()
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
rsp = NewRecorder()
// Get a session.
if session, err = store.Get(req, "session-key"); err != nil {
t.Fatalf("Error getting session: %v", err)
}
// Get a flash.
flashes = session.Flashes()
if len(flashes) != 0 {
t.Errorf("Expected empty flashes; Got %v", flashes)
}
// Add some flashes.
session.AddFlash("foo")
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
hdr = rsp.Header()
cookies, ok = hdr["Set-Cookie"]
if !ok || len(cookies) != 1 {
t.Fatalf("No cookies. Header:", hdr)
}
// Get a session.
req.Header.Add("Cookie", cookies[0])
if session, err = store.Get(req, "session-key"); err != nil {
t.Fatalf("Error getting session: %v", err)
}
// Check all saved values.
flashes = session.Flashes()
if len(flashes) != 1 {
t.Fatalf("Expected flashes; Got %v", flashes)
}
if flashes[0] != "foo" {
t.Errorf("Expected foo,bar; Got %v", flashes)
}
}
func TestPingGoodPort(t *testing.T) {
store, _ := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
defer store.Close()
ok, err := store.ping()
if err != nil {
t.Error(err.Error())
}
if !ok {
t.Error("Expected server to PONG")
}
}
func TestPingBadPort(t *testing.T) {
store, _ := NewRediStore(10, "tcp", ":6378", "", []byte("secret-key"))
defer store.Close()
_, err := store.ping()
if err == nil {
t.Error("Expected error")
}
}
func ExampleRediStore() {
// RedisStore
store, err := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
if err != nil {
panic(err)
}
defer store.Close()
}
func init() {
gob.Register(FlashMessage{})
}

1
vendor/github.com/boombuler/barcode/.gitignore generated vendored Normal file
View File

@@ -0,0 +1 @@
.vscode/

21
vendor/github.com/boombuler/barcode/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Florian Sundermann
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.

53
vendor/github.com/boombuler/barcode/README.md generated vendored Normal file
View File

@@ -0,0 +1,53 @@
[![Join the chat at https://gitter.im/golang-barcode/Lobby](https://badges.gitter.im/golang-barcode/Lobby.svg)](https://gitter.im/golang-barcode/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Introduction ##
This is a package for GO which can be used to create different types of barcodes.
## Supported Barcode Types ##
* 2 of 5
* Aztec Code
* Codabar
* Code 128
* Code 39
* Code 93
* Datamatrix
* EAN 13
* EAN 8
* PDF 417
* QR Code
## Example ##
This is a simple example on how to create a QR-Code and write it to a png-file
```go
package main
import (
"image/png"
"os"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr"
)
func main() {
// Create the barcode
qrCode, _ := qr.Encode("Hello World", qr.M, qr.Auto)
// Scale the barcode to 200x200 pixels
qrCode, _ = barcode.Scale(qrCode, 200, 200)
// create the output file
file, _ := os.Create("qrcode.png")
defer file.Close()
// encode the barcode as png
png.Encode(file, qrCode)
}
```
## Documentation ##
See [GoDoc](https://godoc.org/github.com/boombuler/barcode)
To create a barcode use the Encode function from one of the subpackages.

View File

@@ -0,0 +1,94 @@
package aztec
import (
"testing"
)
func encodeTest(t *testing.T, data, wanted string) {
result, err := Encode([]byte(data), DEFAULT_EC_PERCENT, DEFAULT_LAYERS)
if err != nil {
t.Error(err)
} else {
ac, ok := result.(*aztecCode)
if !ok {
t.Error("returned barcode is no aztec code...")
} else if draw := ac.string(); draw != wanted {
t.Errorf("Invalid Barcode returned:\n%s", draw)
}
}
}
func Test_Encode1(t *testing.T) {
encodeTest(t, "This is an example Aztec symbol for Wikipedia.",
"X X X X X X X X \n"+
"X X X X X X X X X X \n"+
"X X X X X X X X X X X \n"+
"X X X X X X X X X X X \n"+
" X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X \n"+
"X X X X X X X X X X \n"+
" X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X \n"+
" X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X \n"+
" X X X \n"+
" X X X X X X X X X X \n"+
" X X X X X X X X X X \n")
}
func Test_Encode2(t *testing.T) {
encodeTest(t, "Aztec Code is a public domain 2D matrix barcode symbology"+
" of nominally square symbols built on a square grid with a "+
"distinctive square bullseye pattern at their center.",
" X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X \n")
}

62
vendor/github.com/boombuler/barcode/aztec/azteccode.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package aztec
import (
"bytes"
"image"
"image/color"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
type aztecCode struct {
*utils.BitList
size int
content []byte
}
func newAztecCode(size int) *aztecCode {
return &aztecCode{utils.NewBitList(size * size), size, nil}
}
func (c *aztecCode) Content() string {
return string(c.content)
}
func (c *aztecCode) Metadata() barcode.Metadata {
return barcode.Metadata{barcode.TypeAztec, 2}
}
func (c *aztecCode) ColorModel() color.Model {
return color.Gray16Model
}
func (c *aztecCode) Bounds() image.Rectangle {
return image.Rect(0, 0, c.size, c.size)
}
func (c *aztecCode) At(x, y int) color.Color {
if c.GetBit(x*c.size + y) {
return color.Black
}
return color.White
}
func (c *aztecCode) set(x, y int) {
c.SetBit(x*c.size+y, true)
}
func (c *aztecCode) string() string {
buf := new(bytes.Buffer)
for y := 0; y < c.size; y++ {
for x := 0; x < c.size; x++ {
if c.GetBit(x*c.size + y) {
buf.WriteString("X ")
} else {
buf.WriteString(" ")
}
}
buf.WriteRune('\n')
}
return buf.String()
}

268
vendor/github.com/boombuler/barcode/aztec/encoder.go generated vendored Normal file
View File

@@ -0,0 +1,268 @@
// Package aztec can create Aztec Code barcodes
package aztec
import (
"fmt"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
const (
DEFAULT_EC_PERCENT = 33
DEFAULT_LAYERS = 0
max_nb_bits = 32
max_nb_bits_compact = 4
)
var (
word_size = []int{
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
}
)
func totalBitsInLayer(layers int, compact bool) int {
tmp := 112
if compact {
tmp = 88
}
return (tmp + 16*layers) * layers
}
func stuffBits(bits *utils.BitList, wordSize int) *utils.BitList {
out := new(utils.BitList)
n := bits.Len()
mask := (1 << uint(wordSize)) - 2
for i := 0; i < n; i += wordSize {
word := 0
for j := 0; j < wordSize; j++ {
if i+j >= n || bits.GetBit(i+j) {
word |= 1 << uint(wordSize-1-j)
}
}
if (word & mask) == mask {
out.AddBits(word&mask, byte(wordSize))
i--
} else if (word & mask) == 0 {
out.AddBits(word|1, byte(wordSize))
i--
} else {
out.AddBits(word, byte(wordSize))
}
}
return out
}
func generateModeMessage(compact bool, layers, messageSizeInWords int) *utils.BitList {
modeMessage := new(utils.BitList)
if compact {
modeMessage.AddBits(layers-1, 2)
modeMessage.AddBits(messageSizeInWords-1, 6)
modeMessage = generateCheckWords(modeMessage, 28, 4)
} else {
modeMessage.AddBits(layers-1, 5)
modeMessage.AddBits(messageSizeInWords-1, 11)
modeMessage = generateCheckWords(modeMessage, 40, 4)
}
return modeMessage
}
func drawModeMessage(matrix *aztecCode, compact bool, matrixSize int, modeMessage *utils.BitList) {
center := matrixSize / 2
if compact {
for i := 0; i < 7; i++ {
offset := center - 3 + i
if modeMessage.GetBit(i) {
matrix.set(offset, center-5)
}
if modeMessage.GetBit(i + 7) {
matrix.set(center+5, offset)
}
if modeMessage.GetBit(20 - i) {
matrix.set(offset, center+5)
}
if modeMessage.GetBit(27 - i) {
matrix.set(center-5, offset)
}
}
} else {
for i := 0; i < 10; i++ {
offset := center - 5 + i + i/5
if modeMessage.GetBit(i) {
matrix.set(offset, center-7)
}
if modeMessage.GetBit(i + 10) {
matrix.set(center+7, offset)
}
if modeMessage.GetBit(29 - i) {
matrix.set(offset, center+7)
}
if modeMessage.GetBit(39 - i) {
matrix.set(center-7, offset)
}
}
}
}
func drawBullsEye(matrix *aztecCode, center, size int) {
for i := 0; i < size; i += 2 {
for j := center - i; j <= center+i; j++ {
matrix.set(j, center-i)
matrix.set(j, center+i)
matrix.set(center-i, j)
matrix.set(center+i, j)
}
}
matrix.set(center-size, center-size)
matrix.set(center-size+1, center-size)
matrix.set(center-size, center-size+1)
matrix.set(center+size, center-size)
matrix.set(center+size, center-size+1)
matrix.set(center+size, center+size-1)
}
// Encode returns an aztec barcode with the given content
func Encode(data []byte, minECCPercent int, userSpecifiedLayers int) (barcode.Barcode, error) {
bits := highlevelEncode(data)
eccBits := ((bits.Len() * minECCPercent) / 100) + 11
totalSizeBits := bits.Len() + eccBits
var layers, TotalBitsInLayer, wordSize int
var compact bool
var stuffedBits *utils.BitList
if userSpecifiedLayers != DEFAULT_LAYERS {
compact = userSpecifiedLayers < 0
if compact {
layers = -userSpecifiedLayers
} else {
layers = userSpecifiedLayers
}
if (compact && layers > max_nb_bits_compact) || (!compact && layers > max_nb_bits) {
return nil, fmt.Errorf("Illegal value %d for layers", userSpecifiedLayers)
}
TotalBitsInLayer = totalBitsInLayer(layers, compact)
wordSize = word_size[layers]
usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize)
stuffedBits = stuffBits(bits, wordSize)
if stuffedBits.Len()+eccBits > usableBitsInLayers {
return nil, fmt.Errorf("Data to large for user specified layer")
}
if compact && stuffedBits.Len() > wordSize*64 {
return nil, fmt.Errorf("Data to large for user specified layer")
}
} else {
wordSize = 0
stuffedBits = nil
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
// is the same size, but has more data.
for i := 0; ; i++ {
if i > max_nb_bits {
return nil, fmt.Errorf("Data too large for an aztec code")
}
compact = i <= 3
layers = i
if compact {
layers = i + 1
}
TotalBitsInLayer = totalBitsInLayer(layers, compact)
if totalSizeBits > TotalBitsInLayer {
continue
}
// [Re]stuff the bits if this is the first opportunity, or if the
// wordSize has changed
if wordSize != word_size[layers] {
wordSize = word_size[layers]
stuffedBits = stuffBits(bits, wordSize)
}
usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize)
if compact && stuffedBits.Len() > wordSize*64 {
// Compact format only allows 64 data words, though C4 can hold more words than that
continue
}
if stuffedBits.Len()+eccBits <= usableBitsInLayers {
break
}
}
}
messageBits := generateCheckWords(stuffedBits, TotalBitsInLayer, wordSize)
messageSizeInWords := stuffedBits.Len() / wordSize
modeMessage := generateModeMessage(compact, layers, messageSizeInWords)
// allocate symbol
var baseMatrixSize int
if compact {
baseMatrixSize = 11 + layers*4
} else {
baseMatrixSize = 14 + layers*4
}
alignmentMap := make([]int, baseMatrixSize)
var matrixSize int
if compact {
// no alignment marks in compact mode, alignmentMap is a no-op
matrixSize = baseMatrixSize
for i := 0; i < len(alignmentMap); i++ {
alignmentMap[i] = i
}
} else {
matrixSize = baseMatrixSize + 1 + 2*((baseMatrixSize/2-1)/15)
origCenter := baseMatrixSize / 2
center := matrixSize / 2
for i := 0; i < origCenter; i++ {
newOffset := i + i/15
alignmentMap[origCenter-i-1] = center - newOffset - 1
alignmentMap[origCenter+i] = center + newOffset + 1
}
}
code := newAztecCode(matrixSize)
code.content = data
// draw data bits
for i, rowOffset := 0, 0; i < layers; i++ {
rowSize := (layers - i) * 4
if compact {
rowSize += 9
} else {
rowSize += 12
}
for j := 0; j < rowSize; j++ {
columnOffset := j * 2
for k := 0; k < 2; k++ {
if messageBits.GetBit(rowOffset + columnOffset + k) {
code.set(alignmentMap[i*2+k], alignmentMap[i*2+j])
}
if messageBits.GetBit(rowOffset + rowSize*2 + columnOffset + k) {
code.set(alignmentMap[i*2+j], alignmentMap[baseMatrixSize-1-i*2-k])
}
if messageBits.GetBit(rowOffset + rowSize*4 + columnOffset + k) {
code.set(alignmentMap[baseMatrixSize-1-i*2-k], alignmentMap[baseMatrixSize-1-i*2-j])
}
if messageBits.GetBit(rowOffset + rowSize*6 + columnOffset + k) {
code.set(alignmentMap[baseMatrixSize-1-i*2-j], alignmentMap[i*2+k])
}
}
}
rowOffset += rowSize * 8
}
// draw mode message
drawModeMessage(code, compact, matrixSize, modeMessage)
// draw alignment marks
if compact {
drawBullsEye(code, matrixSize/2, 5)
} else {
drawBullsEye(code, matrixSize/2, 7)
for i, j := 0, 0; i < baseMatrixSize/2-1; i, j = i+15, j+16 {
for k := (matrixSize / 2) & 1; k < matrixSize; k += 2 {
code.set(matrixSize/2-j, k)
code.set(matrixSize/2+j, k)
code.set(k, matrixSize/2-j)
code.set(k, matrixSize/2+j)
}
}
}
return code, nil
}

View File

@@ -0,0 +1,58 @@
package aztec
import (
"strings"
"testing"
"github.com/boombuler/barcode/utils"
)
func Test_StuffBits(t *testing.T) {
testStuffBits := func(wordSize int, bits string, expected string) {
bl := new(utils.BitList)
for _, r := range bits {
if r == 'X' {
bl.AddBit(true)
} else if r == '.' {
bl.AddBit(false)
}
}
stuffed := stuffBits(bl, wordSize)
expectedBits := strings.Replace(expected, " ", "", -1)
result := bitStr(stuffed)
if result != expectedBits {
t.Errorf("stuffBits failed for %q\nGot: %q", bits, result)
}
}
testStuffBits(5, ".X.X. X.X.X .X.X.",
".X.X. X.X.X .X.X.")
testStuffBits(5, ".X.X. ..... .X.X",
".X.X. ....X ..X.X")
testStuffBits(3, "XX. ... ... ..X XXX .X. ..",
"XX. ..X ..X ..X ..X .XX XX. .X. ..X")
testStuffBits(6, ".X.X.. ...... ..X.XX",
".X.X.. .....X. ..X.XX XXXX.")
testStuffBits(6, ".X.X.. ...... ...... ..X.X.",
".X.X.. .....X .....X ....X. X.XXXX")
testStuffBits(6, ".X.X.. XXXXXX ...... ..X.XX",
".X.X.. XXXXX. X..... ...X.X XXXXX.")
testStuffBits(6,
"...... ..XXXX X..XX. .X.... .X.X.X .....X .X.... ...X.X .....X ....XX ..X... ....X. X..XXX X.XX.X",
".....X ...XXX XX..XX ..X... ..X.X. X..... X.X... ....X. X..... X....X X..X.. .....X X.X..X XXX.XX .XXXXX")
}
func Test_ModeMessage(t *testing.T) {
testModeMessage := func(compact bool, layers, words int, expected string) {
result := bitStr(generateModeMessage(compact, layers, words))
expectedBits := strings.Replace(expected, " ", "", -1)
if result != expectedBits {
t.Errorf("generateModeMessage(%v, %d, %d) failed.\nGot:%s", compact, layers, words, result)
}
}
testModeMessage(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X")
testModeMessage(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX..")
testModeMessage(false, 21, 660, "X.X.. .X.X..X..XX .XXX ..X.. .XXX. .X... ..XXX")
testModeMessage(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX")
}

View File

@@ -0,0 +1,61 @@
package aztec
import (
"github.com/boombuler/barcode/utils"
)
func bitsToWords(stuffedBits *utils.BitList, wordSize int, wordCount int) []int {
message := make([]int, wordCount)
for i := 0; i < wordCount; i++ {
value := 0
for j := 0; j < wordSize; j++ {
if stuffedBits.GetBit(i*wordSize + j) {
value |= (1 << uint(wordSize-j-1))
}
}
message[i] = value
}
return message
}
func generateCheckWords(bits *utils.BitList, totalBits, wordSize int) *utils.BitList {
rs := utils.NewReedSolomonEncoder(getGF(wordSize))
// bits is guaranteed to be a multiple of the wordSize, so no padding needed
messageWordCount := bits.Len() / wordSize
totalWordCount := totalBits / wordSize
eccWordCount := totalWordCount - messageWordCount
messageWords := bitsToWords(bits, wordSize, messageWordCount)
eccWords := rs.Encode(messageWords, eccWordCount)
startPad := totalBits % wordSize
messageBits := new(utils.BitList)
messageBits.AddBits(0, byte(startPad))
for _, messageWord := range messageWords {
messageBits.AddBits(messageWord, byte(wordSize))
}
for _, eccWord := range eccWords {
messageBits.AddBits(eccWord, byte(wordSize))
}
return messageBits
}
func getGF(wordSize int) *utils.GaloisField {
switch wordSize {
case 4:
return utils.NewGaloisField(0x13, 16, 1)
case 6:
return utils.NewGaloisField(0x43, 64, 1)
case 8:
return utils.NewGaloisField(0x012D, 256, 1)
case 10:
return utils.NewGaloisField(0x409, 1024, 1)
case 12:
return utils.NewGaloisField(0x1069, 4096, 1)
default:
return nil
}
}

171
vendor/github.com/boombuler/barcode/aztec/highlevel.go generated vendored Normal file
View File

@@ -0,0 +1,171 @@
package aztec
import (
"github.com/boombuler/barcode/utils"
)
func highlevelEncode(data []byte) *utils.BitList {
states := stateSlice{initialState}
for index := 0; index < len(data); index++ {
pairCode := 0
nextChar := byte(0)
if index+1 < len(data) {
nextChar = data[index+1]
}
switch cur := data[index]; {
case cur == '\r' && nextChar == '\n':
pairCode = 2
case cur == '.' && nextChar == ' ':
pairCode = 3
case cur == ',' && nextChar == ' ':
pairCode = 4
case cur == ':' && nextChar == ' ':
pairCode = 5
}
if pairCode > 0 {
// We have one of the four special PUNCT pairs. Treat them specially.
// Get a new set of states for the two new characters.
states = updateStateListForPair(states, data, index, pairCode)
index++
} else {
// Get a new set of states for the new character.
states = updateStateListForChar(states, data, index)
}
}
minBitCnt := int((^uint(0)) >> 1)
var result *state = nil
for _, s := range states {
if s.bitCount < minBitCnt {
minBitCnt = s.bitCount
result = s
}
}
if result != nil {
return result.toBitList(data)
} else {
return new(utils.BitList)
}
}
func simplifyStates(states stateSlice) stateSlice {
var result stateSlice = nil
for _, newState := range states {
add := true
var newResult stateSlice = nil
for _, oldState := range result {
if add && oldState.isBetterThanOrEqualTo(newState) {
add = false
}
if !(add && newState.isBetterThanOrEqualTo(oldState)) {
newResult = append(newResult, oldState)
}
}
if add {
result = append(newResult, newState)
} else {
result = newResult
}
}
return result
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
func updateStateListForChar(states stateSlice, data []byte, index int) stateSlice {
var result stateSlice = nil
for _, s := range states {
if r := updateStateForChar(s, data, index); len(r) > 0 {
result = append(result, r...)
}
}
return simplifyStates(result)
}
// Return a set of states that represent the possible ways of updating this
// state for the next character. The resulting set of states are added to
// the "result" list.
func updateStateForChar(s *state, data []byte, index int) stateSlice {
var result stateSlice = nil
ch := data[index]
charInCurrentTable := charMap[s.mode][ch] > 0
var stateNoBinary *state = nil
for mode := mode_upper; mode <= mode_punct; mode++ {
charInMode := charMap[mode][ch]
if charInMode > 0 {
if stateNoBinary == nil {
// Only create stateNoBinary the first time it's required.
stateNoBinary = s.endBinaryShift(index)
}
// Try generating the character by latching to its mode
if !charInCurrentTable || mode == s.mode || mode == mode_digit {
// If the character is in the current table, we don't want to latch to
// any other mode except possibly digit (which uses only 4 bits). Any
// other latch would be equally successful *after* this character, and
// so wouldn't save any bits.
res := stateNoBinary.latchAndAppend(mode, charInMode)
result = append(result, res)
}
// Try generating the character by switching to its mode.
if _, ok := shiftTable[s.mode][mode]; !charInCurrentTable && ok {
// It never makes sense to temporarily shift to another mode if the
// character exists in the current mode. That can never save bits.
res := stateNoBinary.shiftAndAppend(mode, charInMode)
result = append(result, res)
}
}
}
if s.bShiftByteCount > 0 || charMap[s.mode][ch] == 0 {
// It's never worthwhile to go into binary shift mode if you're not already
// in binary shift mode, and the character exists in your current mode.
// That can never save bits over just outputting the char in the current mode.
res := s.addBinaryShiftChar(index)
result = append(result, res)
}
return result
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
func updateStateListForPair(states stateSlice, data []byte, index int, pairCode int) stateSlice {
var result stateSlice = nil
for _, s := range states {
if r := updateStateForPair(s, data, index, pairCode); len(r) > 0 {
result = append(result, r...)
}
}
return simplifyStates(result)
}
func updateStateForPair(s *state, data []byte, index int, pairCode int) stateSlice {
var result stateSlice
stateNoBinary := s.endBinaryShift(index)
// Possibility 1. Latch to MODE_PUNCT, and then append this code
result = append(result, stateNoBinary.latchAndAppend(mode_punct, pairCode))
if s.mode != mode_punct {
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
// Every state except MODE_PUNCT (handled above) can shift
result = append(result, stateNoBinary.shiftAndAppend(mode_punct, pairCode))
}
if pairCode == 3 || pairCode == 4 {
// both characters are in DIGITS. Sometimes better to just add two digits
digitState := stateNoBinary.
latchAndAppend(mode_digit, 16-pairCode). // period or comma in DIGIT
latchAndAppend(mode_digit, 1) // space in DIGIT
result = append(result, digitState)
}
if s.bShiftByteCount > 0 {
// It only makes sense to do the characters as binary if we're already
// in binary mode.
result = append(result, s.addBinaryShiftChar(index).addBinaryShiftChar(index+1))
}
return result
}

View File

@@ -0,0 +1,132 @@
package aztec
import (
"bytes"
"strings"
"testing"
"github.com/boombuler/barcode/utils"
)
func bitStr(bl *utils.BitList) string {
buf := new(bytes.Buffer)
for i := 0; i < bl.Len(); i++ {
if bl.GetBit(i) {
buf.WriteRune('X')
} else {
buf.WriteRune('.')
}
}
return buf.String()
}
func testHighLevelEncodeString(t *testing.T, s, expectedBits string) {
bits := highlevelEncode([]byte(s))
result := bitStr(bits)
expectedBits = strings.Replace(expectedBits, " ", "", -1)
if result != expectedBits {
t.Errorf("invalid result for highlevelEncode(%q). Got:\n%s", s, result)
}
}
func testHighLevelEncodeStringCnt(t *testing.T, s string, expectedBitCnt int) {
bits := highlevelEncode([]byte(s))
if bits.Len() != expectedBitCnt {
t.Errorf("invalid result for highlevelEncode(%q). Got %d, expected %d bits", s, bits.Len(), expectedBitCnt)
}
}
func Test_HighLevelEncode(t *testing.T) {
testHighLevelEncodeString(t, "A. b.",
// 'A' P/S '. ' L/L b D/L '.'
"...X. ..... ...XX XXX.. ...XX XXXX. XX.X")
testHighLevelEncodeString(t, "Lorem ipsum.",
// 'L' L/L 'o' 'r' 'e' 'm' ' ' 'i' 'p' 's' 'u' 'm' D/L '.'
".XX.X XXX.. X.... X..XX ..XX. .XXX. ....X .X.X. X...X X.X.. X.XX. .XXX. XXXX. XX.X")
testHighLevelEncodeString(t, "Lo. Test 123.",
// 'L' L/L 'o' P/S '. ' U/S 'T' 'e' 's' 't' D/L ' ' '1' '2' '3' '.'
".XX.X XXX.. X.... ..... ...XX XXX.. X.X.X ..XX. X.X.. X.X.X XXXX. ...X ..XX .X.. .X.X XX.X")
testHighLevelEncodeString(t, "Lo...x",
// 'L' L/L 'o' D/L '.' '.' '.' U/L L/L 'x'
".XX.X XXX.. X.... XXXX. XX.X XX.X XX.X XXX. XXX.. XX..X")
testHighLevelEncodeString(t, ". x://abc/.",
//P/S '. ' L/L 'x' P/S ':' P/S '/' P/S '/' 'a' 'b' 'c' P/S '/' D/L '.'
"..... ...XX XXX.. XX..X ..... X.X.X ..... X.X.. ..... X.X.. ...X. ...XX ..X.. ..... X.X.. XXXX. XX.X")
// Uses Binary/Shift rather than Lower/Shift to save two bits.
testHighLevelEncodeString(t, "ABCdEFG",
//'A' 'B' 'C' B/S =1 'd' 'E' 'F' 'G'
"...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X...")
testHighLevelEncodeStringCnt(t,
// Found on an airline boarding pass. Several stretches of Binary shift are
// necessary to keep the bitcount so low.
"09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEi"+
"Ygh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
823)
}
func Test_HighLevelEncodeBinary(t *testing.T) {
// binary short form single byte
testHighLevelEncodeString(t, "N\u0000N",
// 'N' B/S =1 '\0' N
".XXXX XXXXX ....X ........ .XXXX") // Encode "N" in UPPER
testHighLevelEncodeString(t, "N\u0000n",
// 'N' B/S =2 '\0' 'n'
".XXXX XXXXX ...X. ........ .XX.XXX.") // Encode "n" in BINARY
// binary short form consecutive bytes
testHighLevelEncodeString(t, "N\x00\x80 A",
// 'N' B/S =2 '\0' \u0080 ' ' 'A'
".XXXX XXXXX ...X. ........ X....... ....X ...X.")
// binary skipping over single character
testHighLevelEncodeString(t, "\x00a\xFF\x80 A",
// B/S =4 '\0' 'a' '\3ff' '\200' ' ' 'A'
"XXXXX ..X.. ........ .XX....X XXXXXXXX X....... ....X ...X.")
// getting into binary mode from digit mode
testHighLevelEncodeString(t, "1234\u0000",
//D/L '1' '2' '3' '4' U/L B/S =1 \0
"XXXX. ..XX .X.. .X.X .XX. XXX. XXXXX ....X ........")
// Create a string in which every character requires binary
sb := new(bytes.Buffer)
for i := 0; i <= 3000; i++ {
sb.WriteByte(byte(128 + (i % 30)))
}
// Test the output generated by Binary/Switch, particularly near the
// places where the encoding changes: 31, 62, and 2047+31=2078
for _, i := range []int{1, 2, 3, 10, 29, 30, 31, 32, 33, 60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100} {
// This is the expected length of a binary string of length "i"
expectedLength := (8 * i)
switch {
case i <= 31:
expectedLength += 10
case i <= 62:
expectedLength += 20
case i <= 2078:
expectedLength += 21
default:
expectedLength += 31
}
data := string(sb.Bytes()[:i])
// Verify that we are correct about the length.
testHighLevelEncodeStringCnt(t, data, expectedLength)
if i != 1 && i != 32 && i != 2079 {
// The addition of an 'a' at the beginning or end gets merged into the binary code
// in those cases where adding another binary character only adds 8 or 9 bits to the result.
// So we exclude the border cases i=1,32,2079
// A lower case letter at the beginning will be merged into binary mode
testHighLevelEncodeStringCnt(t, "a"+string(sb.Bytes()[:i-1]), expectedLength)
// A lower case letter at the end will also be merged into binary mode
testHighLevelEncodeStringCnt(t, string(sb.Bytes()[:i-1])+"a", expectedLength)
}
// A lower case letter at both ends will enough to latch us into LOWER.
testHighLevelEncodeStringCnt(t, "a"+data+"b", expectedLength+15)
}
}

264
vendor/github.com/boombuler/barcode/aztec/state.go generated vendored Normal file
View File

@@ -0,0 +1,264 @@
package aztec
import (
"fmt"
"github.com/boombuler/barcode/utils"
)
type encodingMode byte
const (
mode_upper encodingMode = iota // 5 bits
mode_lower // 5 bits
mode_digit // 4 bits
mode_mixed // 5 bits
mode_punct // 5 bits
)
var (
// The Latch Table shows, for each pair of Modes, the optimal method for
// getting from one mode to another. In the worst possible case, this can
// be up to 14 bits. In the best possible case, we are already there!
// The high half-word of each entry gives the number of bits.
// The low half-word of each entry are the actual bits necessary to change
latchTable = map[encodingMode]map[encodingMode]int{
mode_upper: {
mode_upper: 0,
mode_lower: (5 << 16) + 28,
mode_digit: (5 << 16) + 30,
mode_mixed: (5 << 16) + 29,
mode_punct: (10 << 16) + (29 << 5) + 30,
},
mode_lower: {
mode_upper: (9 << 16) + (30 << 4) + 14,
mode_lower: 0,
mode_digit: (5 << 16) + 30,
mode_mixed: (5 << 16) + 29,
mode_punct: (10 << 16) + (29 << 5) + 30,
},
mode_digit: {
mode_upper: (4 << 16) + 14,
mode_lower: (9 << 16) + (14 << 5) + 28,
mode_digit: 0,
mode_mixed: (9 << 16) + (14 << 5) + 29,
mode_punct: (14 << 16) + (14 << 10) + (29 << 5) + 30,
},
mode_mixed: {
mode_upper: (5 << 16) + 29,
mode_lower: (5 << 16) + 28,
mode_digit: (10 << 16) + (29 << 5) + 30,
mode_mixed: 0,
mode_punct: (5 << 16) + 30,
},
mode_punct: {
mode_upper: (5 << 16) + 31,
mode_lower: (10 << 16) + (31 << 5) + 28,
mode_digit: (10 << 16) + (31 << 5) + 30,
mode_mixed: (10 << 16) + (31 << 5) + 29,
mode_punct: 0,
},
}
// A map showing the available shift codes. (The shifts to BINARY are not shown)
shiftTable = map[encodingMode]map[encodingMode]int{
mode_upper: {
mode_punct: 0,
},
mode_lower: {
mode_punct: 0,
mode_upper: 28,
},
mode_mixed: {
mode_punct: 0,
},
mode_digit: {
mode_punct: 0,
mode_upper: 15,
},
}
charMap map[encodingMode][]int
)
type state struct {
mode encodingMode
tokens token
bShiftByteCount int
bitCount int
}
type stateSlice []*state
var initialState *state = &state{
mode: mode_upper,
tokens: nil,
bShiftByteCount: 0,
bitCount: 0,
}
func init() {
charMap = make(map[encodingMode][]int)
charMap[mode_upper] = make([]int, 256)
charMap[mode_lower] = make([]int, 256)
charMap[mode_digit] = make([]int, 256)
charMap[mode_mixed] = make([]int, 256)
charMap[mode_punct] = make([]int, 256)
charMap[mode_upper][' '] = 1
for c := 'A'; c <= 'Z'; c++ {
charMap[mode_upper][int(c)] = int(c - 'A' + 2)
}
charMap[mode_lower][' '] = 1
for c := 'a'; c <= 'z'; c++ {
charMap[mode_lower][c] = int(c - 'a' + 2)
}
charMap[mode_digit][' '] = 1
for c := '0'; c <= '9'; c++ {
charMap[mode_digit][c] = int(c - '0' + 2)
}
charMap[mode_digit][','] = 12
charMap[mode_digit]['.'] = 13
mixedTable := []int{
0, ' ', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 27, 28, 29, 30, 31, '@', '\\', '^',
'_', '`', '|', '~', 127,
}
for i, v := range mixedTable {
charMap[mode_mixed][v] = i
}
punctTable := []int{
0, '\r', 0, 0, 0, 0, '!', '\'', '#', '$', '%', '&', '\'',
'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
'[', ']', '{', '}',
}
for i, v := range punctTable {
if v > 0 {
charMap[mode_punct][v] = i
}
}
}
func (em encodingMode) BitCount() byte {
if em == mode_digit {
return 4
}
return 5
}
// Create a new state representing this state with a latch to a (not
// necessary different) mode, and then a code.
func (s *state) latchAndAppend(mode encodingMode, value int) *state {
bitCount := s.bitCount
tokens := s.tokens
if mode != s.mode {
latch := latchTable[s.mode][mode]
tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16))
bitCount += latch >> 16
}
tokens = newSimpleToken(tokens, value, mode.BitCount())
return &state{
mode: mode,
tokens: tokens,
bShiftByteCount: 0,
bitCount: bitCount + int(mode.BitCount()),
}
}
// Create a new state representing this state, with a temporary shift
// to a different mode to output a single value.
func (s *state) shiftAndAppend(mode encodingMode, value int) *state {
tokens := s.tokens
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
tokens = newSimpleToken(tokens, shiftTable[s.mode][mode], s.mode.BitCount())
tokens = newSimpleToken(tokens, value, 5)
return &state{
mode: s.mode,
tokens: tokens,
bShiftByteCount: 0,
bitCount: s.bitCount + int(s.mode.BitCount()) + 5,
}
}
// Create a new state representing this state, but an additional character
// output in Binary Shift mode.
func (s *state) addBinaryShiftChar(index int) *state {
tokens := s.tokens
mode := s.mode
bitCnt := s.bitCount
if s.mode == mode_punct || s.mode == mode_digit {
latch := latchTable[s.mode][mode_upper]
tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16))
bitCnt += latch >> 16
mode = mode_upper
}
deltaBitCount := 8
if s.bShiftByteCount == 0 || s.bShiftByteCount == 31 {
deltaBitCount = 18
} else if s.bShiftByteCount == 62 {
deltaBitCount = 9
}
result := &state{
mode: mode,
tokens: tokens,
bShiftByteCount: s.bShiftByteCount + 1,
bitCount: bitCnt + deltaBitCount,
}
if result.bShiftByteCount == 2047+31 {
// The string is as long as it's allowed to be. We should end it.
result = result.endBinaryShift(index + 1)
}
return result
}
// Create the state identical to this one, but we are no longer in
// Binary Shift mode.
func (s *state) endBinaryShift(index int) *state {
if s.bShiftByteCount == 0 {
return s
}
tokens := newShiftToken(s.tokens, index-s.bShiftByteCount, s.bShiftByteCount)
return &state{
mode: s.mode,
tokens: tokens,
bShiftByteCount: 0,
bitCount: s.bitCount,
}
}
// Returns true if "this" state is better (or equal) to be in than "that"
// state under all possible circumstances.
func (this *state) isBetterThanOrEqualTo(other *state) bool {
mySize := this.bitCount + (latchTable[this.mode][other.mode] >> 16)
if other.bShiftByteCount > 0 && (this.bShiftByteCount == 0 || this.bShiftByteCount > other.bShiftByteCount) {
mySize += 10 // Cost of entering Binary Shift mode.
}
return mySize <= other.bitCount
}
func (s *state) toBitList(text []byte) *utils.BitList {
tokens := make([]token, 0)
se := s.endBinaryShift(len(text))
for t := se.tokens; t != nil; t = t.prev() {
tokens = append(tokens, t)
}
res := new(utils.BitList)
for i := len(tokens) - 1; i >= 0; i-- {
tokens[i].appendTo(res, text)
}
return res
}
func (s *state) String() string {
tokens := make([]token, 0)
for t := s.tokens; t != nil; t = t.prev() {
tokens = append([]token{t}, tokens...)
}
return fmt.Sprintf("M:%d bits=%d bytes=%d: %v", s.mode, s.bitCount, s.bShiftByteCount, tokens)
}

75
vendor/github.com/boombuler/barcode/aztec/token.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
package aztec
import (
"fmt"
"github.com/boombuler/barcode/utils"
)
type token interface {
fmt.Stringer
prev() token
appendTo(bits *utils.BitList, text []byte)
}
type simpleToken struct {
token
value int
bitCount byte
}
type binaryShiftToken struct {
token
bShiftStart int
bShiftByteCnt int
}
func newSimpleToken(prev token, value int, bitCount byte) token {
return &simpleToken{prev, value, bitCount}
}
func newShiftToken(prev token, bShiftStart int, bShiftCnt int) token {
return &binaryShiftToken{prev, bShiftStart, bShiftCnt}
}
func (st *simpleToken) prev() token {
return st.token
}
func (st *simpleToken) appendTo(bits *utils.BitList, text []byte) {
bits.AddBits(st.value, st.bitCount)
}
func (st *simpleToken) String() string {
value := st.value & ((1 << st.bitCount) - 1)
value |= 1 << st.bitCount
return "<" + fmt.Sprintf("%b", value)[1:] + ">"
}
func (bst *binaryShiftToken) prev() token {
return bst.token
}
func (bst *binaryShiftToken) appendTo(bits *utils.BitList, text []byte) {
for i := 0; i < bst.bShiftByteCnt; i++ {
if i == 0 || (i == 31 && bst.bShiftByteCnt <= 62) {
// We need a header before the first character, and before
// character 31 when the total byte code is <= 62
bits.AddBits(31, 5) // BINARY_SHIFT
if bst.bShiftByteCnt > 62 {
bits.AddBits(bst.bShiftByteCnt-31, 16)
} else if i == 0 {
// 1 <= binaryShiftByteCode <= 62
if bst.bShiftByteCnt < 31 {
bits.AddBits(bst.bShiftByteCnt, 5)
} else {
bits.AddBits(31, 5)
}
} else {
// 32 <= binaryShiftCount <= 62 and i == 31
bits.AddBits(bst.bShiftByteCnt-31, 5)
}
}
bits.AddByte(text[bst.bShiftStart+i])
}
}
func (bst *binaryShiftToken) String() string {
return fmt.Sprintf("<%d::%d>", bst.bShiftStart, (bst.bShiftStart + bst.bShiftByteCnt - 1))
}

42
vendor/github.com/boombuler/barcode/barcode.go generated vendored Normal file
View File

@@ -0,0 +1,42 @@
package barcode
import "image"
const (
TypeAztec = "Aztec"
TypeCodabar = "Codabar"
TypeCode128 = "Code 128"
TypeCode39 = "Code 39"
TypeCode93 = "Code 93"
TypeDataMatrix = "DataMatrix"
TypeEAN8 = "EAN 8"
TypeEAN13 = "EAN 13"
TypePDF = "PDF417"
TypeQR = "QR Code"
Type2of5 = "2 of 5"
Type2of5Interleaved = "2 of 5 (interleaved)"
)
// Contains some meta information about a barcode
type Metadata struct {
// the name of the barcode kind
CodeKind string
// contains 1 for 1D barcodes or 2 for 2D barcodes
Dimensions byte
}
// a rendered and encoded barcode
type Barcode interface {
image.Image
// returns some meta information about the barcode
Metadata() Metadata
// the data that was encoded in this barcode
Content() string
}
// Additional interface that some barcodes might implement to provide
// the value of its checksum.
type BarcodeIntCS interface {
Barcode
CheckSum() int
}

49
vendor/github.com/boombuler/barcode/codabar/encoder.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
// Package codabar can create Codabar barcodes
package codabar
import (
"fmt"
"regexp"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
var encodingTable = map[rune][]bool{
'0': []bool{true, false, true, false, true, false, false, true, true},
'1': []bool{true, false, true, false, true, true, false, false, true},
'2': []bool{true, false, true, false, false, true, false, true, true},
'3': []bool{true, true, false, false, true, false, true, false, true},
'4': []bool{true, false, true, true, false, true, false, false, true},
'5': []bool{true, true, false, true, false, true, false, false, true},
'6': []bool{true, false, false, true, false, true, false, true, true},
'7': []bool{true, false, false, true, false, true, true, false, true},
'8': []bool{true, false, false, true, true, false, true, false, true},
'9': []bool{true, true, false, true, false, false, true, false, true},
'-': []bool{true, false, true, false, false, true, true, false, true},
'$': []bool{true, false, true, true, false, false, true, false, true},
':': []bool{true, true, false, true, false, true, true, false, true, true},
'/': []bool{true, true, false, true, true, false, true, false, true, true},
'.': []bool{true, true, false, true, true, false, true, true, false, true},
'+': []bool{true, false, true, true, false, false, true, true, false, false, true, true},
'A': []bool{true, false, true, true, false, false, true, false, false, true},
'B': []bool{true, false, true, false, false, true, false, false, true, true},
'C': []bool{true, false, false, true, false, false, true, false, true, true},
'D': []bool{true, false, true, false, false, true, true, false, false, true},
}
// Encode creates a codabar barcode for the given content
func Encode(content string) (barcode.Barcode, error) {
checkValid, _ := regexp.Compile(`[ABCD][0123456789\-\$\:/\.\+]*[ABCD]$`)
if content == "!" || checkValid.ReplaceAllString(content, "!") != "!" {
return nil, fmt.Errorf("can not encode \"%s\"", content)
}
resBits := new(utils.BitList)
for i, r := range content {
if i > 0 {
resBits.AddBit(false)
}
resBits.AddBit(encodingTable[r]...)
}
return utils.New1DCode(barcode.TypeCodabar, content, resBits), nil
}

View File

@@ -0,0 +1,32 @@
package codabar
import (
"image/color"
"testing"
)
func Test_Encode(t *testing.T) {
_, err := Encode("FOOBAR")
if err == nil {
t.Error("\"FOOBAR\" should not be encodable")
}
testEncode := func(txt, testResult string) {
code, err := Encode(txt)
if err != nil || code == nil {
t.Fail()
} else {
if code.Bounds().Max.X != len(testResult) {
t.Errorf("%v: length missmatch", txt)
} else {
for i, r := range testResult {
if (code.At(i, 0) == color.Black) != (r == '1') {
t.Errorf("%v: code missmatch on position %d", txt, i)
}
}
}
}
}
testEncode("A40156B", "10110010010101101001010101001101010110010110101001010010101101010010011")
}

203
vendor/github.com/boombuler/barcode/code128/encode.go generated vendored Normal file
View File

@@ -0,0 +1,203 @@
// Package code128 can create Code128 barcodes
package code128
import (
"fmt"
"strings"
"unicode/utf8"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
func strToRunes(str string) []rune {
result := make([]rune, utf8.RuneCountInString(str))
i := 0
for _, r := range str {
result[i] = r
i++
}
return result
}
func shouldUseCTable(nextRunes []rune, curEncoding byte) bool {
requiredDigits := 4
if curEncoding == startCSymbol {
requiredDigits = 2
}
if len(nextRunes) < requiredDigits {
return false
}
for i := 0; i < requiredDigits; i++ {
if i%2 == 0 && nextRunes[i] == FNC1 {
requiredDigits++
if len(nextRunes) < requiredDigits {
return false
}
continue
}
if nextRunes[i] < '0' || nextRunes[i] > '9' {
return false
}
}
return true
}
func tableContainsRune(table string, r rune) bool {
return strings.ContainsRune(table, r) || r == FNC1 || r == FNC2 || r == FNC3 || r == FNC4
}
func shouldUseATable(nextRunes []rune, curEncoding byte) bool {
nextRune := nextRunes[0]
if !tableContainsRune(bTable, nextRune) || curEncoding == startASymbol {
return tableContainsRune(aTable, nextRune)
}
if curEncoding == 0 {
for _, r := range nextRunes {
if tableContainsRune(abTable, r) {
continue
}
if strings.ContainsRune(aOnlyTable, r) {
return true
}
break
}
}
return false
}
func getCodeIndexList(content []rune) *utils.BitList {
result := new(utils.BitList)
curEncoding := byte(0)
for i := 0; i < len(content); i++ {
if shouldUseCTable(content[i:], curEncoding) {
if curEncoding != startCSymbol {
if curEncoding == byte(0) {
result.AddByte(startCSymbol)
} else {
result.AddByte(codeCSymbol)
}
curEncoding = startCSymbol
}
if content[i] == FNC1 {
result.AddByte(102)
} else {
idx := (content[i] - '0') * 10
i++
idx = idx + (content[i] - '0')
result.AddByte(byte(idx))
}
} else if shouldUseATable(content[i:], curEncoding) {
if curEncoding != startASymbol {
if curEncoding == byte(0) {
result.AddByte(startASymbol)
} else {
result.AddByte(codeASymbol)
}
curEncoding = startASymbol
}
var idx int
switch content[i] {
case FNC1:
idx = 102
break
case FNC2:
idx = 97
break
case FNC3:
idx = 96
break
case FNC4:
idx = 101
break
default:
idx = strings.IndexRune(aTable, content[i])
break
}
if idx < 0 {
return nil
}
result.AddByte(byte(idx))
} else {
if curEncoding != startBSymbol {
if curEncoding == byte(0) {
result.AddByte(startBSymbol)
} else {
result.AddByte(codeBSymbol)
}
curEncoding = startBSymbol
}
var idx int
switch content[i] {
case FNC1:
idx = 102
break
case FNC2:
idx = 97
break
case FNC3:
idx = 96
break
case FNC4:
idx = 100
break
default:
idx = strings.IndexRune(bTable, content[i])
break
}
if idx < 0 {
return nil
}
result.AddByte(byte(idx))
}
}
return result
}
// Encode creates a Code 128 barcode for the given content
func Encode(content string) (barcode.BarcodeIntCS, error) {
contentRunes := strToRunes(content)
if len(contentRunes) <= 0 || len(contentRunes) > 80 {
return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes))
}
idxList := getCodeIndexList(contentRunes)
if idxList == nil {
return nil, fmt.Errorf("\"%s\" could not be encoded", content)
}
result := new(utils.BitList)
sum := 0
for i, idx := range idxList.GetBytes() {
if i == 0 {
sum = int(idx)
} else {
sum += i * int(idx)
}
result.AddBit(encodingTable[idx]...)
}
sum = sum % 103
result.AddBit(encodingTable[sum]...)
result.AddBit(encodingTable[stopSymbol]...)
return utils.New1DCodeIntCheckSum(barcode.TypeCode128, content, result, sum), nil
}
func EncodeWithoutChecksum(content string) (barcode.Barcode, error) {
contentRunes := strToRunes(content)
if len(contentRunes) <= 0 || len(contentRunes) > 80 {
return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes))
}
idxList := getCodeIndexList(contentRunes)
if idxList == nil {
return nil, fmt.Errorf("\"%s\" could not be encoded", content)
}
result := new(utils.BitList)
for _, idx := range idxList.GetBytes() {
result.AddBit(encodingTable[idx]...)
}
result.AddBit(encodingTable[stopSymbol]...)
return utils.New1DCode(barcode.TypeCode128, content, result), nil
}

View File

@@ -0,0 +1,131 @@
package code128
import (
"image/color"
"testing"
)
func testEncode(t *testing.T, txt, testResult string) {
code, err := Encode(txt)
if err != nil || code == nil {
t.Error(err)
} else {
if code.Bounds().Max.X != len(testResult) {
t.Errorf("%v: length missmatch. Got %d expected %d", txt, code.Bounds().Max.X, len(testResult))
} else {
encoded := ""
failed := false
for i, r := range testResult {
if code.At(i, 0) == color.Black {
encoded += "1"
} else {
encoded += "0"
}
if (code.At(i, 0) == color.Black) != (r == '1') {
failed = true
t.Errorf("%v: code missmatch on position %d", txt, i)
}
}
if failed {
t.Log("Encoded: ", encoded)
}
}
}
}
func Test_EncodeFunctionChars(t *testing.T) {
encFNC1 := "11110101110"
encFNC2 := "11110101000"
encFNC3 := "10111100010"
encFNC4 := "10111101110"
encStartB := "11010010000"
encStop := "1100011101011"
// Special Case FC1 can also be encoded to C Table therefor using 123 as suffix might have unexpected results.
testEncode(t, string(FNC1)+"A23", encStartB+encFNC1+"10100011000"+"11001110010"+"11001011100"+"10100011110"+encStop)
testEncode(t, string(FNC2)+"123", encStartB+encFNC2+"10011100110"+"11001110010"+"11001011100"+"11100010110"+encStop)
testEncode(t, string(FNC3)+"123", encStartB+encFNC3+"10011100110"+"11001110010"+"11001011100"+"11101000110"+encStop)
testEncode(t, string(FNC4)+"123", encStartB+encFNC4+"10011100110"+"11001110010"+"11001011100"+"11100011010"+encStop)
}
func Test_Unencodable(t *testing.T) {
if _, err := Encode(""); err == nil {
t.Fail()
}
if _, err := Encode("ä"); err == nil {
t.Fail()
}
}
func Test_EncodeCTable(t *testing.T) {
testEncode(t, "HI345678H", "110100100001100010100011000100010101110111101000101100011100010110110000101001011110111011000101000111011000101100011101011")
testEncode(t, "334455", "11010011100101000110001000110111011101000110100100111101100011101011")
testEncode(t, string(FNC1)+"1234",
"11010011100"+ // Start C
"11110101110"+ // FNC1
"10110011100"+ // 12
"10001011000"+ // 34
"11101001100"+ // CheckSum == 24
"1100011101011") // Stop
}
func Test_shouldUseCTable(t *testing.T) {
if !shouldUseCTable([]rune{FNC1, '1', '2'}, startCSymbol) {
t.Error("[FNC1]12 failed")
}
if shouldUseCTable([]rune{FNC1, '1'}, startCSymbol) {
t.Error("[FNC1]1 failed")
}
if shouldUseCTable([]rune{'0', FNC1, '1'}, startCSymbol) {
t.Error("0[FNC1]1 failed")
}
if !shouldUseCTable([]rune{'0', '1', FNC1, '2', '3'}, startBSymbol) {
t.Error("01[FNC1]23 failed")
}
if shouldUseCTable([]rune{'0', '1', FNC1}, startBSymbol) {
t.Error("01[FNC1] failed")
}
}
func Test_Issue16(t *testing.T) {
if !shouldUseATable([]rune{'\r', 'A'}, 0) {
t.Error("Code should start with A-Table if the text start with \\r")
}
if !shouldUseATable([]rune{FNC1, '\r'}, 0) {
t.Error("Code should start with A-Table if the text start with <FNC1>\\r")
}
if shouldUseATable([]rune{FNC1, '1', '2', '3'}, 0) {
t.Error("Code should not start with A-Table if the text start with <FNC1>123")
}
testEncode(t, string(FNC3)+"$P\rI", "110100001001011110001010010001100111011101101111011101011000100010110001010001100011101011")
}
func Test_Datalogic(t *testing.T) {
// <Start A><FNC3>$P\r<checksum><STOP>
testEncode(t, string(FNC3)+"$P\r",
"11010000100"+ // <Start A>
"10111100010"+ // <FNC3>
"10010001100"+ // $
"11101110110"+ // P
"11110111010"+ // CR
"11000100010"+ // checksum = 'I'
"1100011101011") // STOP
// <Start B><FNC3>$P,Ae,P<CR><checksum><STOP>
testEncode(t, string(FNC3)+"$P,Ae,P\r",
"11010010000"+ // <Start B>
"10111100010"+ // <FNC3>
"10010001100"+ // $
"11101110110"+ // P
"10110011100"+ // ,
"10100011000"+ // A
"10110010000"+ // e
"10110011100"+ // ,
"11101110110"+ // P
"11101011110"+ // <Code A>
"11110111010"+ // <CR>
"10110001000"+ // checksum = 'D'
"1100011101011") // STOP
}

View File

@@ -0,0 +1,143 @@
package code128
var encodingTable = [107][]bool{
[]bool{true, true, false, true, true, false, false, true, true, false, false},
[]bool{true, true, false, false, true, true, false, true, true, false, false},
[]bool{true, true, false, false, true, true, false, false, true, true, false},
[]bool{true, false, false, true, false, false, true, true, false, false, false},
[]bool{true, false, false, true, false, false, false, true, true, false, false},
[]bool{true, false, false, false, true, false, false, true, true, false, false},
[]bool{true, false, false, true, true, false, false, true, false, false, false},
[]bool{true, false, false, true, true, false, false, false, true, false, false},
[]bool{true, false, false, false, true, true, false, false, true, false, false},
[]bool{true, true, false, false, true, false, false, true, false, false, false},
[]bool{true, true, false, false, true, false, false, false, true, false, false},
[]bool{true, true, false, false, false, true, false, false, true, false, false},
[]bool{true, false, true, true, false, false, true, true, true, false, false},
[]bool{true, false, false, true, true, false, true, true, true, false, false},
[]bool{true, false, false, true, true, false, false, true, true, true, false},
[]bool{true, false, true, true, true, false, false, true, true, false, false},
[]bool{true, false, false, true, true, true, false, true, true, false, false},
[]bool{true, false, false, true, true, true, false, false, true, true, false},
[]bool{true, true, false, false, true, true, true, false, false, true, false},
[]bool{true, true, false, false, true, false, true, true, true, false, false},
[]bool{true, true, false, false, true, false, false, true, true, true, false},
[]bool{true, true, false, true, true, true, false, false, true, false, false},
[]bool{true, true, false, false, true, true, true, false, true, false, false},
[]bool{true, true, true, false, true, true, false, true, true, true, false},
[]bool{true, true, true, false, true, false, false, true, true, false, false},
[]bool{true, true, true, false, false, true, false, true, true, false, false},
[]bool{true, true, true, false, false, true, false, false, true, true, false},
[]bool{true, true, true, false, true, true, false, false, true, false, false},
[]bool{true, true, true, false, false, true, true, false, true, false, false},
[]bool{true, true, true, false, false, true, true, false, false, true, false},
[]bool{true, true, false, true, true, false, true, true, false, false, false},
[]bool{true, true, false, true, true, false, false, false, true, true, false},
[]bool{true, true, false, false, false, true, true, false, true, true, false},
[]bool{true, false, true, false, false, false, true, true, false, false, false},
[]bool{true, false, false, false, true, false, true, true, false, false, false},
[]bool{true, false, false, false, true, false, false, false, true, true, false},
[]bool{true, false, true, true, false, false, false, true, false, false, false},
[]bool{true, false, false, false, true, true, false, true, false, false, false},
[]bool{true, false, false, false, true, true, false, false, false, true, false},
[]bool{true, true, false, true, false, false, false, true, false, false, false},
[]bool{true, true, false, false, false, true, false, true, false, false, false},
[]bool{true, true, false, false, false, true, false, false, false, true, false},
[]bool{true, false, true, true, false, true, true, true, false, false, false},
[]bool{true, false, true, true, false, false, false, true, true, true, false},
[]bool{true, false, false, false, true, true, false, true, true, true, false},
[]bool{true, false, true, true, true, false, true, true, false, false, false},
[]bool{true, false, true, true, true, false, false, false, true, true, false},
[]bool{true, false, false, false, true, true, true, false, true, true, false},
[]bool{true, true, true, false, true, true, true, false, true, true, false},
[]bool{true, true, false, true, false, false, false, true, true, true, false},
[]bool{true, true, false, false, false, true, false, true, true, true, false},
[]bool{true, true, false, true, true, true, false, true, false, false, false},
[]bool{true, true, false, true, true, true, false, false, false, true, false},
[]bool{true, true, false, true, true, true, false, true, true, true, false},
[]bool{true, true, true, false, true, false, true, true, false, false, false},
[]bool{true, true, true, false, true, false, false, false, true, true, false},
[]bool{true, true, true, false, false, false, true, false, true, true, false},
[]bool{true, true, true, false, true, true, false, true, false, false, false},
[]bool{true, true, true, false, true, true, false, false, false, true, false},
[]bool{true, true, true, false, false, false, true, true, false, true, false},
[]bool{true, true, true, false, true, true, true, true, false, true, false},
[]bool{true, true, false, false, true, false, false, false, false, true, false},
[]bool{true, true, true, true, false, false, false, true, false, true, false},
[]bool{true, false, true, false, false, true, true, false, false, false, false},
[]bool{true, false, true, false, false, false, false, true, true, false, false},
[]bool{true, false, false, true, false, true, true, false, false, false, false},
[]bool{true, false, false, true, false, false, false, false, true, true, false},
[]bool{true, false, false, false, false, true, false, true, true, false, false},
[]bool{true, false, false, false, false, true, false, false, true, true, false},
[]bool{true, false, true, true, false, false, true, false, false, false, false},
[]bool{true, false, true, true, false, false, false, false, true, false, false},
[]bool{true, false, false, true, true, false, true, false, false, false, false},
[]bool{true, false, false, true, true, false, false, false, false, true, false},
[]bool{true, false, false, false, false, true, true, false, true, false, false},
[]bool{true, false, false, false, false, true, true, false, false, true, false},
[]bool{true, true, false, false, false, false, true, false, false, true, false},
[]bool{true, true, false, false, true, false, true, false, false, false, false},
[]bool{true, true, true, true, false, true, true, true, false, true, false},
[]bool{true, true, false, false, false, false, true, false, true, false, false},
[]bool{true, false, false, false, true, true, true, true, false, true, false},
[]bool{true, false, true, false, false, true, true, true, true, false, false},
[]bool{true, false, false, true, false, true, true, true, true, false, false},
[]bool{true, false, false, true, false, false, true, true, true, true, false},
[]bool{true, false, true, true, true, true, false, false, true, false, false},
[]bool{true, false, false, true, true, true, true, false, true, false, false},
[]bool{true, false, false, true, true, true, true, false, false, true, false},
[]bool{true, true, true, true, false, true, false, false, true, false, false},
[]bool{true, true, true, true, false, false, true, false, true, false, false},
[]bool{true, true, true, true, false, false, true, false, false, true, false},
[]bool{true, true, false, true, true, false, true, true, true, true, false},
[]bool{true, true, false, true, true, true, true, false, true, true, false},
[]bool{true, true, true, true, false, true, true, false, true, true, false},
[]bool{true, false, true, false, true, true, true, true, false, false, false},
[]bool{true, false, true, false, false, false, true, true, true, true, false},
[]bool{true, false, false, false, true, false, true, true, true, true, false},
[]bool{true, false, true, true, true, true, false, true, false, false, false},
[]bool{true, false, true, true, true, true, false, false, false, true, false},
[]bool{true, true, true, true, false, true, false, true, false, false, false},
[]bool{true, true, true, true, false, true, false, false, false, true, false},
[]bool{true, false, true, true, true, false, true, true, true, true, false},
[]bool{true, false, true, true, true, true, false, true, true, true, false},
[]bool{true, true, true, false, true, false, true, true, true, true, false},
[]bool{true, true, true, true, false, true, false, true, true, true, false},
[]bool{true, true, false, true, false, false, false, false, true, false, false},
[]bool{true, true, false, true, false, false, true, false, false, false, false},
[]bool{true, true, false, true, false, false, true, true, true, false, false},
[]bool{true, true, false, false, false, true, true, true, false, true, false, true, true},
}
const startASymbol byte = 103
const startBSymbol byte = 104
const startCSymbol byte = 105
const codeASymbol byte = 101
const codeBSymbol byte = 100
const codeCSymbol byte = 99
const stopSymbol byte = 106
const (
// FNC1 - Special Function 1
FNC1 = '\u00f1'
// FNC2 - Special Function 2
FNC2 = '\u00f2'
// FNC3 - Special Function 3
FNC3 = '\u00f3'
// FNC4 - Special Function 4
FNC4 = '\u00f4'
)
const abTable = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
const bTable = abTable + "`abcdefghijklmnopqrstuvwxyz{|}~\u007F"
const aOnlyTable = "\u0000\u0001\u0002\u0003\u0004" + // NUL, SOH, STX, ETX, EOT
"\u0005\u0006\u0007\u0008\u0009" + // ENQ, ACK, BEL, BS, HT
"\u000A\u000B\u000C\u000D\u000E" + // LF, VT, FF, CR, SO
"\u000F\u0010\u0011\u0012\u0013" + // SI, DLE, DC1, DC2, DC3
"\u0014\u0015\u0016\u0017\u0018" + // DC4, NAK, SYN, ETB, CAN
"\u0019\u001A\u001B\u001C\u001D" + // EM, SUB, ESC, FS, GS
"\u001E\u001F" // RS, US
const aTable = abTable + aOnlyTable

152
vendor/github.com/boombuler/barcode/code39/encoder.go generated vendored Normal file
View File

@@ -0,0 +1,152 @@
// Package code39 can create Code39 barcodes
package code39
import (
"errors"
"strconv"
"strings"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
type encodeInfo struct {
value int
data []bool
}
var encodeTable = map[rune]encodeInfo{
'0': encodeInfo{0, []bool{true, false, true, false, false, true, true, false, true, true, false, true}},
'1': encodeInfo{1, []bool{true, true, false, true, false, false, true, false, true, false, true, true}},
'2': encodeInfo{2, []bool{true, false, true, true, false, false, true, false, true, false, true, true}},
'3': encodeInfo{3, []bool{true, true, false, true, true, false, false, true, false, true, false, true}},
'4': encodeInfo{4, []bool{true, false, true, false, false, true, true, false, true, false, true, true}},
'5': encodeInfo{5, []bool{true, true, false, true, false, false, true, true, false, true, false, true}},
'6': encodeInfo{6, []bool{true, false, true, true, false, false, true, true, false, true, false, true}},
'7': encodeInfo{7, []bool{true, false, true, false, false, true, false, true, true, false, true, true}},
'8': encodeInfo{8, []bool{true, true, false, true, false, false, true, false, true, true, false, true}},
'9': encodeInfo{9, []bool{true, false, true, true, false, false, true, false, true, true, false, true}},
'A': encodeInfo{10, []bool{true, true, false, true, false, true, false, false, true, false, true, true}},
'B': encodeInfo{11, []bool{true, false, true, true, false, true, false, false, true, false, true, true}},
'C': encodeInfo{12, []bool{true, true, false, true, true, false, true, false, false, true, false, true}},
'D': encodeInfo{13, []bool{true, false, true, false, true, true, false, false, true, false, true, true}},
'E': encodeInfo{14, []bool{true, true, false, true, false, true, true, false, false, true, false, true}},
'F': encodeInfo{15, []bool{true, false, true, true, false, true, true, false, false, true, false, true}},
'G': encodeInfo{16, []bool{true, false, true, false, true, false, false, true, true, false, true, true}},
'H': encodeInfo{17, []bool{true, true, false, true, false, true, false, false, true, true, false, true}},
'I': encodeInfo{18, []bool{true, false, true, true, false, true, false, false, true, true, false, true}},
'J': encodeInfo{19, []bool{true, false, true, false, true, true, false, false, true, true, false, true}},
'K': encodeInfo{20, []bool{true, true, false, true, false, true, false, true, false, false, true, true}},
'L': encodeInfo{21, []bool{true, false, true, true, false, true, false, true, false, false, true, true}},
'M': encodeInfo{22, []bool{true, true, false, true, true, false, true, false, true, false, false, true}},
'N': encodeInfo{23, []bool{true, false, true, false, true, true, false, true, false, false, true, true}},
'O': encodeInfo{24, []bool{true, true, false, true, false, true, true, false, true, false, false, true}},
'P': encodeInfo{25, []bool{true, false, true, true, false, true, true, false, true, false, false, true}},
'Q': encodeInfo{26, []bool{true, false, true, false, true, false, true, true, false, false, true, true}},
'R': encodeInfo{27, []bool{true, true, false, true, false, true, false, true, true, false, false, true}},
'S': encodeInfo{28, []bool{true, false, true, true, false, true, false, true, true, false, false, true}},
'T': encodeInfo{29, []bool{true, false, true, false, true, true, false, true, true, false, false, true}},
'U': encodeInfo{30, []bool{true, true, false, false, true, false, true, false, true, false, true, true}},
'V': encodeInfo{31, []bool{true, false, false, true, true, false, true, false, true, false, true, true}},
'W': encodeInfo{32, []bool{true, true, false, false, true, true, false, true, false, true, false, true}},
'X': encodeInfo{33, []bool{true, false, false, true, false, true, true, false, true, false, true, true}},
'Y': encodeInfo{34, []bool{true, true, false, false, true, false, true, true, false, true, false, true}},
'Z': encodeInfo{35, []bool{true, false, false, true, true, false, true, true, false, true, false, true}},
'-': encodeInfo{36, []bool{true, false, false, true, false, true, false, true, true, false, true, true}},
'.': encodeInfo{37, []bool{true, true, false, false, true, false, true, false, true, true, false, true}},
' ': encodeInfo{38, []bool{true, false, false, true, true, false, true, false, true, true, false, true}},
'$': encodeInfo{39, []bool{true, false, false, true, false, false, true, false, false, true, false, true}},
'/': encodeInfo{40, []bool{true, false, false, true, false, false, true, false, true, false, false, true}},
'+': encodeInfo{41, []bool{true, false, false, true, false, true, false, false, true, false, false, true}},
'%': encodeInfo{42, []bool{true, false, true, false, false, true, false, false, true, false, false, true}},
'*': encodeInfo{-1, []bool{true, false, false, true, false, true, true, false, true, true, false, true}},
}
var extendedTable = map[rune]string{
0: `%U`, 1: `$A`, 2: `$B`, 3: `$C`, 4: `$D`, 5: `$E`, 6: `$F`, 7: `$G`, 8: `$H`, 9: `$I`, 10: `$J`,
11: `$K`, 12: `$L`, 13: `$M`, 14: `$N`, 15: `$O`, 16: `$P`, 17: `$Q`, 18: `$R`, 19: `$S`, 20: `$T`,
21: `$U`, 22: `$V`, 23: `$W`, 24: `$X`, 25: `$Y`, 26: `$Z`, 27: `%A`, 28: `%B`, 29: `%C`, 30: `%D`,
31: `%E`, 33: `/A`, 34: `/B`, 35: `/C`, 36: `/D`, 37: `/E`, 38: `/F`, 39: `/G`, 40: `/H`, 41: `/I`,
42: `/J`, 43: `/K`, 44: `/L`, 47: `/O`, 58: `/Z`, 59: `%F`, 60: `%G`, 61: `%H`, 62: `%I`, 63: `%J`,
64: `%V`, 91: `%K`, 92: `%L`, 93: `%M`, 94: `%N`, 95: `%O`, 96: `%W`, 97: `+A`, 98: `+B`, 99: `+C`,
100: `+D`, 101: `+E`, 102: `+F`, 103: `+G`, 104: `+H`, 105: `+I`, 106: `+J`, 107: `+K`, 108: `+L`,
109: `+M`, 110: `+N`, 111: `+O`, 112: `+P`, 113: `+Q`, 114: `+R`, 115: `+S`, 116: `+T`, 117: `+U`,
118: `+V`, 119: `+W`, 120: `+X`, 121: `+Y`, 122: `+Z`, 123: `%P`, 124: `%Q`, 125: `%R`, 126: `%S`,
127: `%T`,
}
func getChecksum(content string) string {
sum := 0
for _, r := range content {
info, ok := encodeTable[r]
if !ok || info.value < 0 {
return "#"
}
sum += info.value
}
sum = sum % 43
for r, v := range encodeTable {
if v.value == sum {
return string(r)
}
}
return "#"
}
func prepare(content string) (string, error) {
result := ""
for _, r := range content {
if r > 127 {
return "", errors.New("Only ASCII strings can be encoded")
}
val, ok := extendedTable[r]
if ok {
result += val
} else {
result += string([]rune{r})
}
}
return result, nil
}
// Encode returns a code39 barcode for the given content
// if includeChecksum is set to true, a checksum character is calculated and added to the content
func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.BarcodeIntCS, error) {
if fullASCIIMode {
var err error
content, err = prepare(content)
if err != nil {
return nil, err
}
} else if strings.ContainsRune(content, '*') {
return nil, errors.New("invalid data! try full ascii mode")
}
data := "*" + content
if includeChecksum {
data += getChecksum(content)
}
data += "*"
result := new(utils.BitList)
for i, r := range data {
if i != 0 {
result.AddBit(false)
}
info, ok := encodeTable[r]
if !ok {
return nil, errors.New("invalid data! try full ascii mode")
}
result.AddBit(info.data...)
}
checkSum, err := strconv.ParseInt(getChecksum(content), 10, 64)
if err != nil {
checkSum = 0
}
return utils.New1DCodeIntCheckSum(barcode.TypeCode39, content, result, int(checkSum)), nil
}

View File

@@ -0,0 +1,31 @@
package code39
import (
"image/color"
"testing"
)
func doTest(t *testing.T, addCS, fullASCII bool, data, testResult string) {
code, err := Encode(data, addCS, fullASCII)
if err != nil {
t.Error(err)
}
if len(testResult) != code.Bounds().Max.X {
t.Errorf("Invalid code size. Expected %d got %d", len(testResult), code.Bounds().Max.X)
}
for i, r := range testResult {
if (code.At(i, 0) == color.Black) != (r == '1') {
t.Errorf("Failed at position %d", i)
}
}
}
func Test_Encode(t *testing.T) {
doTest(t, false, false, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"1001011011010110101001011010110100101101101101001010101011001011011010110010101"+
"011011001010101010011011011010100110101011010011010101011001101011010101001101011010"+
"100110110110101001010101101001101101011010010101101101001010101011001101101010110010"+
"101101011001010101101100101100101010110100110101011011001101010101001011010110110010"+
"110101010011011010101010011011010110100101011010110010101101101100101010101001101011"+
"011010011010101011001101010101001011011011010010110101011001011010100101101101")
}

131
vendor/github.com/boombuler/barcode/code93/encoder.go generated vendored Normal file
View File

@@ -0,0 +1,131 @@
// Package code93 can create Code93 barcodes
package code93
import (
"errors"
"strings"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
type encodeInfo struct {
value int
data int
}
const (
// Special Function 1 ($)
FNC1 = '\u00f1'
// Special Function 2 (%)
FNC2 = '\u00f2'
// Special Function 3 (/)
FNC3 = '\u00f3'
// Special Function 4 (+)
FNC4 = '\u00f4'
)
var encodeTable = map[rune]encodeInfo{
'0': encodeInfo{0, 0x114}, '1': encodeInfo{1, 0x148}, '2': encodeInfo{2, 0x144},
'3': encodeInfo{3, 0x142}, '4': encodeInfo{4, 0x128}, '5': encodeInfo{5, 0x124},
'6': encodeInfo{6, 0x122}, '7': encodeInfo{7, 0x150}, '8': encodeInfo{8, 0x112},
'9': encodeInfo{9, 0x10A}, 'A': encodeInfo{10, 0x1A8}, 'B': encodeInfo{11, 0x1A4},
'C': encodeInfo{12, 0x1A2}, 'D': encodeInfo{13, 0x194}, 'E': encodeInfo{14, 0x192},
'F': encodeInfo{15, 0x18A}, 'G': encodeInfo{16, 0x168}, 'H': encodeInfo{17, 0x164},
'I': encodeInfo{18, 0x162}, 'J': encodeInfo{19, 0x134}, 'K': encodeInfo{20, 0x11A},
'L': encodeInfo{21, 0x158}, 'M': encodeInfo{22, 0x14C}, 'N': encodeInfo{23, 0x146},
'O': encodeInfo{24, 0x12C}, 'P': encodeInfo{25, 0x116}, 'Q': encodeInfo{26, 0x1B4},
'R': encodeInfo{27, 0x1B2}, 'S': encodeInfo{28, 0x1AC}, 'T': encodeInfo{29, 0x1A6},
'U': encodeInfo{30, 0x196}, 'V': encodeInfo{31, 0x19A}, 'W': encodeInfo{32, 0x16C},
'X': encodeInfo{33, 0x166}, 'Y': encodeInfo{34, 0x136}, 'Z': encodeInfo{35, 0x13A},
'-': encodeInfo{36, 0x12E}, '.': encodeInfo{37, 0x1D4}, ' ': encodeInfo{38, 0x1D2},
'$': encodeInfo{39, 0x1CA}, '/': encodeInfo{40, 0x16E}, '+': encodeInfo{41, 0x176},
'%': encodeInfo{42, 0x1AE}, FNC1: encodeInfo{43, 0x126}, FNC2: encodeInfo{44, 0x1DA},
FNC3: encodeInfo{45, 0x1D6}, FNC4: encodeInfo{46, 0x132}, '*': encodeInfo{47, 0x15E},
}
var extendedTable = []string{
"\u00f2U", "\u00f1A", "\u00f1B", "\u00f1C", "\u00f1D", "\u00f1E", "\u00f1F", "\u00f1G",
"\u00f1H", "\u00f1I", "\u00f1J", "\u00f1K", "\u00f1L", "\u00f1M", "\u00f1N", "\u00f1O",
"\u00f1P", "\u00f1Q", "\u00f1R", "\u00f1S", "\u00f1T", "\u00f1U", "\u00f1V", "\u00f1W",
"\u00f1X", "\u00f1Y", "\u00f1Z", "\u00f2A", "\u00f2B", "\u00f2C", "\u00f2D", "\u00f2E",
" ", "\u00f3A", "\u00f3B", "\u00f3C", "\u00f3D", "\u00f3E", "\u00f3F", "\u00f3G",
"\u00f3H", "\u00f3I", "\u00f3J", "\u00f3K", "\u00f3L", "-", ".", "\u00f3O",
"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "\u00f3Z", "\u00f2F", "\u00f2G", "\u00f2H", "\u00f2I", "\u00f2J",
"\u00f2V", "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "\u00f2K", "\u00f2L", "\u00f2M", "\u00f2N", "\u00f2O",
"\u00f2W", "\u00f4A", "\u00f4B", "\u00f4C", "\u00f4D", "\u00f4E", "\u00f4F", "\u00f4G",
"\u00f4H", "\u00f4I", "\u00f4J", "\u00f4K", "\u00f4L", "\u00f4M", "\u00f4N", "\u00f4O",
"\u00f4P", "\u00f4Q", "\u00f4R", "\u00f4S", "\u00f4T", "\u00f4U", "\u00f4V", "\u00f4W",
"\u00f4X", "\u00f4Y", "\u00f4Z", "\u00f2P", "\u00f2Q", "\u00f2R", "\u00f2S", "\u00f2T",
}
func prepare(content string) (string, error) {
result := ""
for _, r := range content {
if r > 127 {
return "", errors.New("Only ASCII strings can be encoded")
}
result += extendedTable[int(r)]
}
return result, nil
}
// Encode returns a code93 barcode for the given content
// if includeChecksum is set to true, two checksum characters are calculated and added to the content
func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.Barcode, error) {
if fullASCIIMode {
var err error
content, err = prepare(content)
if err != nil {
return nil, err
}
} else if strings.ContainsRune(content, '*') {
return nil, errors.New("invalid data! content may not contain '*'")
}
data := content + string(getChecksum(content, 20))
data += string(getChecksum(data, 15))
data = "*" + data + "*"
result := new(utils.BitList)
for _, r := range data {
info, ok := encodeTable[r]
if !ok {
return nil, errors.New("invalid data!")
}
result.AddBits(info.data, 9)
}
result.AddBit(true)
return utils.New1DCode(barcode.TypeCode93, content, result), nil
}
func getChecksum(content string, maxWeight int) rune {
weight := 1
total := 0
data := []rune(content)
for i := len(data) - 1; i >= 0; i-- {
r := data[i]
info, ok := encodeTable[r]
if !ok {
return ' '
}
total += info.value * weight
if weight++; weight > maxWeight {
weight = 1
}
}
total = total % 47
for r, info := range encodeTable {
if info.value == total {
return r
}
}
return ' '
}

View File

@@ -0,0 +1,39 @@
package code93
import (
"image/color"
"testing"
)
func doTest(t *testing.T, data, testResult string) {
code, err := Encode(data, true, false)
if err != nil {
t.Error(err)
}
if len(testResult) != code.Bounds().Max.X {
t.Errorf("Invalid code size. Expected %d got %d", len(testResult), code.Bounds().Max.X)
}
for i, r := range testResult {
if (code.At(i, 0) == color.Black) != (r == '1') {
t.Errorf("Failed at position %d", i)
}
}
}
func Test_CheckSum(t *testing.T) {
if r := getChecksum("TEST93", 20); r != '+' {
t.Errorf("Checksum C-Failed. Got %s", string(r))
}
if r := getChecksum("TEST93+", 15); r != '6' {
t.Errorf("Checksum K-Failed. Got %s", string(r))
}
}
func Test_Encode(t *testing.T) {
doTest(t, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"1010111101101010001101001001101000101100101001100100101100010101011010001011001"+
"001011000101001101001000110101010110001010011001010001101001011001000101101101101001"+
"101100101101011001101001101100101101100110101011011001011001101001101101001110101000"+
"101001010010001010001001010000101001010001001001001001000101010100001000100101000010"+
"101001110101010000101010111101")
}

View File

@@ -0,0 +1,213 @@
package datamatrix
import (
"github.com/boombuler/barcode/utils"
"strconv"
)
type setValFunc func(byte)
type codeLayout struct {
matrix *utils.BitList
occupy *utils.BitList
size *dmCodeSize
}
func newCodeLayout(size *dmCodeSize) *codeLayout {
result := new(codeLayout)
result.matrix = utils.NewBitList(size.MatrixColumns() * size.MatrixRows())
result.occupy = utils.NewBitList(size.MatrixColumns() * size.MatrixRows())
result.size = size
return result
}
func (l *codeLayout) Occupied(row, col int) bool {
return l.occupy.GetBit(col + row*l.size.MatrixColumns())
}
func (l *codeLayout) Set(row, col int, value, bitNum byte) {
val := ((value >> (7 - bitNum)) & 1) == 1
if row < 0 {
row += l.size.MatrixRows()
col += 4 - ((l.size.MatrixRows() + 4) % 8)
}
if col < 0 {
col += l.size.MatrixColumns()
row += 4 - ((l.size.MatrixColumns() + 4) % 8)
}
if l.Occupied(row, col) {
panic("Field already occupied row: " + strconv.Itoa(row) + " col: " + strconv.Itoa(col))
}
l.occupy.SetBit(col+row*l.size.MatrixColumns(), true)
l.matrix.SetBit(col+row*l.size.MatrixColumns(), val)
}
func (l *codeLayout) SetSimple(row, col int, value byte) {
l.Set(row-2, col-2, value, 0)
l.Set(row-2, col-1, value, 1)
l.Set(row-1, col-2, value, 2)
l.Set(row-1, col-1, value, 3)
l.Set(row-1, col-0, value, 4)
l.Set(row-0, col-2, value, 5)
l.Set(row-0, col-1, value, 6)
l.Set(row-0, col-0, value, 7)
}
func (l *codeLayout) Corner1(value byte) {
l.Set(l.size.MatrixRows()-1, 0, value, 0)
l.Set(l.size.MatrixRows()-1, 1, value, 1)
l.Set(l.size.MatrixRows()-1, 2, value, 2)
l.Set(0, l.size.MatrixColumns()-2, value, 3)
l.Set(0, l.size.MatrixColumns()-1, value, 4)
l.Set(1, l.size.MatrixColumns()-1, value, 5)
l.Set(2, l.size.MatrixColumns()-1, value, 6)
l.Set(3, l.size.MatrixColumns()-1, value, 7)
}
func (l *codeLayout) Corner2(value byte) {
l.Set(l.size.MatrixRows()-3, 0, value, 0)
l.Set(l.size.MatrixRows()-2, 0, value, 1)
l.Set(l.size.MatrixRows()-1, 0, value, 2)
l.Set(0, l.size.MatrixColumns()-4, value, 3)
l.Set(0, l.size.MatrixColumns()-3, value, 4)
l.Set(0, l.size.MatrixColumns()-2, value, 5)
l.Set(0, l.size.MatrixColumns()-1, value, 6)
l.Set(1, l.size.MatrixColumns()-1, value, 7)
}
func (l *codeLayout) Corner3(value byte) {
l.Set(l.size.MatrixRows()-3, 0, value, 0)
l.Set(l.size.MatrixRows()-2, 0, value, 1)
l.Set(l.size.MatrixRows()-1, 0, value, 2)
l.Set(0, l.size.MatrixColumns()-2, value, 3)
l.Set(0, l.size.MatrixColumns()-1, value, 4)
l.Set(1, l.size.MatrixColumns()-1, value, 5)
l.Set(2, l.size.MatrixColumns()-1, value, 6)
l.Set(3, l.size.MatrixColumns()-1, value, 7)
}
func (l *codeLayout) Corner4(value byte) {
l.Set(l.size.MatrixRows()-1, 0, value, 0)
l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, value, 1)
l.Set(0, l.size.MatrixColumns()-3, value, 2)
l.Set(0, l.size.MatrixColumns()-2, value, 3)
l.Set(0, l.size.MatrixColumns()-1, value, 4)
l.Set(1, l.size.MatrixColumns()-3, value, 5)
l.Set(1, l.size.MatrixColumns()-2, value, 6)
l.Set(1, l.size.MatrixColumns()-1, value, 7)
}
func (l *codeLayout) SetValues(data []byte) {
idx := 0
row := 4
col := 0
for (row < l.size.MatrixRows()) || (col < l.size.MatrixColumns()) {
if (row == l.size.MatrixRows()) && (col == 0) {
l.Corner1(data[idx])
idx++
}
if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%4 != 0) {
l.Corner2(data[idx])
idx++
}
if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%8 == 4) {
l.Corner3(data[idx])
idx++
}
if (row == l.size.MatrixRows()+4) && (col == 2) && (l.size.MatrixColumns()%8 == 0) {
l.Corner4(data[idx])
idx++
}
for true {
if (row < l.size.MatrixRows()) && (col >= 0) && !l.Occupied(row, col) {
l.SetSimple(row, col, data[idx])
idx++
}
row -= 2
col += 2
if (row < 0) || (col >= l.size.MatrixColumns()) {
break
}
}
row += 1
col += 3
for true {
if (row >= 0) && (col < l.size.MatrixColumns()) && !l.Occupied(row, col) {
l.SetSimple(row, col, data[idx])
idx++
}
row += 2
col -= 2
if (row >= l.size.MatrixRows()) || (col < 0) {
break
}
}
row += 3
col += 1
}
if !l.Occupied(l.size.MatrixRows()-1, l.size.MatrixColumns()-1) {
l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, 255, 0)
l.Set(l.size.MatrixRows()-2, l.size.MatrixColumns()-2, 255, 0)
}
}
func (l *codeLayout) Merge() *datamatrixCode {
result := newDataMatrixCode(l.size)
//dotted horizontal lines
for r := 0; r < l.size.Rows; r += (l.size.RegionRows() + 2) {
for c := 0; c < l.size.Columns; c += 2 {
result.set(c, r, true)
}
}
//solid horizontal line
for r := l.size.RegionRows() + 1; r < l.size.Rows; r += (l.size.RegionRows() + 2) {
for c := 0; c < l.size.Columns; c++ {
result.set(c, r, true)
}
}
//dotted vertical lines
for c := l.size.RegionColumns() + 1; c < l.size.Columns; c += (l.size.RegionColumns() + 2) {
for r := 1; r < l.size.Rows; r += 2 {
result.set(c, r, true)
}
}
//solid vertical line
for c := 0; c < l.size.Columns; c += (l.size.RegionColumns() + 2) {
for r := 0; r < l.size.Rows; r++ {
result.set(c, r, true)
}
}
count := 0
for hRegion := 0; hRegion < l.size.RegionCountHorizontal; hRegion++ {
for vRegion := 0; vRegion < l.size.RegionCountVertical; vRegion++ {
for x := 0; x < l.size.RegionColumns(); x++ {
colMatrix := (l.size.RegionColumns() * hRegion) + x
colResult := ((2 + l.size.RegionColumns()) * hRegion) + x + 1
for y := 0; y < l.size.RegionRows(); y++ {
rowMatrix := (l.size.RegionRows() * vRegion) + y
rowResult := ((2 + l.size.RegionRows()) * vRegion) + y + 1
val := l.matrix.GetBit(colMatrix + rowMatrix*l.size.MatrixColumns())
if val {
count++
}
result.set(colResult, rowResult, val)
}
}
}
}
return result
}

View File

@@ -0,0 +1,73 @@
package datamatrix
type dmCodeSize struct {
Rows int
Columns int
RegionCountHorizontal int
RegionCountVertical int
ECCCount int
BlockCount int
}
func (s *dmCodeSize) RegionRows() int {
return (s.Rows - (s.RegionCountHorizontal * 2)) / s.RegionCountHorizontal
}
func (s *dmCodeSize) RegionColumns() int {
return (s.Columns - (s.RegionCountVertical * 2)) / s.RegionCountVertical
}
func (s *dmCodeSize) MatrixRows() int {
return s.RegionRows() * s.RegionCountHorizontal
}
func (s *dmCodeSize) MatrixColumns() int {
return s.RegionColumns() * s.RegionCountVertical
}
func (s *dmCodeSize) DataCodewords() int {
return ((s.MatrixColumns() * s.MatrixRows()) / 8) - s.ECCCount
}
func (s *dmCodeSize) DataCodewordsForBlock(idx int) int {
if s.Rows == 144 && s.Columns == 144 {
// Special Case...
if idx < 8 {
return 156
} else {
return 155
}
}
return s.DataCodewords() / s.BlockCount
}
func (s *dmCodeSize) ErrorCorrectionCodewordsPerBlock() int {
return s.ECCCount / s.BlockCount
}
var codeSizes []*dmCodeSize = []*dmCodeSize{
&dmCodeSize{10, 10, 1, 1, 5, 1},
&dmCodeSize{12, 12, 1, 1, 7, 1},
&dmCodeSize{14, 14, 1, 1, 10, 1},
&dmCodeSize{16, 16, 1, 1, 12, 1},
&dmCodeSize{18, 18, 1, 1, 14, 1},
&dmCodeSize{20, 20, 1, 1, 18, 1},
&dmCodeSize{22, 22, 1, 1, 20, 1},
&dmCodeSize{24, 24, 1, 1, 24, 1},
&dmCodeSize{26, 26, 1, 1, 28, 1},
&dmCodeSize{32, 32, 2, 2, 36, 1},
&dmCodeSize{36, 36, 2, 2, 42, 1},
&dmCodeSize{40, 40, 2, 2, 48, 1},
&dmCodeSize{44, 44, 2, 2, 56, 1},
&dmCodeSize{48, 48, 2, 2, 68, 1},
&dmCodeSize{52, 52, 2, 2, 84, 2},
&dmCodeSize{64, 64, 4, 4, 112, 2},
&dmCodeSize{72, 72, 4, 4, 144, 4},
&dmCodeSize{80, 80, 4, 4, 192, 4},
&dmCodeSize{88, 88, 4, 4, 224, 4},
&dmCodeSize{96, 96, 4, 4, 272, 4},
&dmCodeSize{104, 104, 4, 4, 336, 6},
&dmCodeSize{120, 120, 6, 6, 408, 6},
&dmCodeSize{132, 132, 6, 6, 496, 8},
&dmCodeSize{144, 144, 6, 6, 620, 10},
}

Some files were not shown because too many files have changed in this diff Show More