replace zxq.co/ripple/hanayo
This commit is contained in:
8
vendor/github.com/DataDog/datadog-go/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/DataDog/datadog-go/.travis.yml
generated
vendored
Normal 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
54
vendor/github.com/DataDog/datadog-go/CHANGELOG.md
generated
vendored
Normal 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
19
vendor/github.com/DataDog/datadog-go/LICENSE.txt
generated
vendored
Normal 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
33
vendor/github.com/DataDog/datadog-go/README.md
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
[](https://travis-ci.org/DataDog/datadog-go)
|
||||
# Overview
|
||||
|
||||
Packages in `datadog-go` provide Go clients for various APIs at [DataDog](http://datadoghq.com).
|
||||
|
||||
## Statsd
|
||||
|
||||
[](https://godoc.org/github.com/DataDog/datadog-go/statsd)
|
||||
[](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
52
vendor/github.com/DataDog/datadog-go/statsd/README.md
generated
vendored
Normal 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
580
vendor/github.com/DataDog/datadog-go/statsd/statsd.go
generated
vendored
Normal 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))
|
||||
}
|
||||
}
|
||||
620
vendor/github.com/DataDog/datadog-go/statsd/statsd_test.go
generated
vendored
Normal file
620
vendor/github.com/DataDog/datadog-go/statsd/statsd_test.go
generated
vendored
Normal 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
2
vendor/github.com/RangelReale/osin/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.exe
|
||||
/src
|
||||
7
vendor/github.com/RangelReale/osin/.travis.yml
generated
vendored
Normal file
7
vendor/github.com/RangelReale/osin/.travis.yml
generated
vendored
Normal 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
19
vendor/github.com/RangelReale/osin/CHANGELOG
generated
vendored
Normal 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
9
vendor/github.com/RangelReale/osin/LICENSE
generated
vendored
Normal 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
112
vendor/github.com/RangelReale/osin/README.md
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
OSIN
|
||||
====
|
||||
|
||||
[](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
562
vendor/github.com/RangelReale/osin/access.go
generated
vendored
Normal 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
489
vendor/github.com/RangelReale/osin/access_test.go
generated
vendored
Normal 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
275
vendor/github.com/RangelReale/osin/authorize.go
generated
vendored
Normal 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
296
vendor/github.com/RangelReale/osin/authorize_test.go
generated
vendored
Normal 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
60
vendor/github.com/RangelReale/osin/client.go
generated
vendored
Normal 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
18
vendor/github.com/RangelReale/osin/client_test.go
generated
vendored
Normal 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
82
vendor/github.com/RangelReale/osin/config.go
generated
vendored
Normal 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
52
vendor/github.com/RangelReale/osin/error.go
generated
vendored
Normal 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
|
||||
}
|
||||
411
vendor/github.com/RangelReale/osin/example/complete/complete.go
generated
vendored
Normal file
411
vendor/github.com/RangelReale/osin/example/complete/complete.go
generated
vendored
Normal 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)
|
||||
}
|
||||
130
vendor/github.com/RangelReale/osin/example/goauth2client/goauth2client.go
generated
vendored
Normal file
130
vendor/github.com/RangelReale/osin/example/goauth2client/goauth2client.go
generated
vendored
Normal 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
57
vendor/github.com/RangelReale/osin/example/helper.go
generated
vendored
Normal 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
|
||||
}
|
||||
215
vendor/github.com/RangelReale/osin/example/jwttoken/jwttoken.go
generated
vendored
Normal file
215
vendor/github.com/RangelReale/osin/example/jwttoken/jwttoken.go
generated
vendored
Normal 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-----`)
|
||||
)
|
||||
273
vendor/github.com/RangelReale/osin/example/openidconnect/openidconnect.go
generated
vendored
Normal file
273
vendor/github.com/RangelReale/osin/example/openidconnect/openidconnect.go
generated
vendored
Normal 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-----`)
|
||||
)
|
||||
127
vendor/github.com/RangelReale/osin/example/osincliclient/osincliclient.go
generated
vendored
Normal file
127
vendor/github.com/RangelReale/osin/example/osincliclient/osincliclient.go
generated
vendored
Normal 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)
|
||||
}
|
||||
126
vendor/github.com/RangelReale/osin/example/simple/simple.go
generated
vendored
Normal file
126
vendor/github.com/RangelReale/osin/example/simple/simple.go
generated
vendored
Normal 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)
|
||||
}
|
||||
108
vendor/github.com/RangelReale/osin/example/teststorage.go
generated
vendored
Normal file
108
vendor/github.com/RangelReale/osin/example/teststorage.go
generated
vendored
Normal 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
81
vendor/github.com/RangelReale/osin/info.go
generated
vendored
Normal 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
72
vendor/github.com/RangelReale/osin/info_test.go
generated
vendored
Normal 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
151
vendor/github.com/RangelReale/osin/response.go
generated
vendored
Normal 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
39
vendor/github.com/RangelReale/osin/response_json.go
generated
vendored
Normal 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
|
||||
}
|
||||
115
vendor/github.com/RangelReale/osin/response_json_test.go
generated
vendored
Normal file
115
vendor/github.com/RangelReale/osin/response_json_test.go
generated
vendored
Normal 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
74
vendor/github.com/RangelReale/osin/response_test.go
generated
vendored
Normal 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
32
vendor/github.com/RangelReale/osin/server.go
generated
vendored
Normal 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
59
vendor/github.com/RangelReale/osin/storage.go
generated
vendored
Normal 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
158
vendor/github.com/RangelReale/osin/storage_test.go
generated
vendored
Normal 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
33
vendor/github.com/RangelReale/osin/tokengen.go
generated
vendored
Normal 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
114
vendor/github.com/RangelReale/osin/urivalidate.go
generated
vendored
Normal 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
120
vendor/github.com/RangelReale/osin/urivalidate_test.go
generated
vendored
Normal 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
108
vendor/github.com/RangelReale/osin/util.go
generated
vendored
Normal 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
136
vendor/github.com/RangelReale/osin/util_test.go
generated
vendored
Normal 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
50
vendor/github.com/ansel1/merry/.gitignore
generated
vendored
Normal 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
9
vendor/github.com/ansel1/merry/.travis.yml
generated
vendored
Normal 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
27
vendor/github.com/ansel1/merry/Gopkg.lock
generated
vendored
Normal 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
26
vendor/github.com/ansel1/merry/Gopkg.toml
generated
vendored
Normal 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
21
vendor/github.com/ansel1/merry/LICENSE.MIT
generated
vendored
Normal 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
193
vendor/github.com/ansel1/merry/README.md
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
merry [](https://travis-ci.org/ansel1/merry) [](https://godoc.org/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
556
vendor/github.com/ansel1/merry/errors.go
generated
vendored
Normal 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
556
vendor/github.com/ansel1/merry/errors_test.go
generated
vendored
Normal 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
68
vendor/github.com/ansel1/merry/print.go
generated
vendored
Normal 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
14
vendor/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
Normal 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
|
||||
26
vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md
generated
vendored
Normal file
26
vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md
generated
vendored
Normal 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
21
vendor/github.com/asaskevich/govalidator/LICENSE
generated
vendored
Normal 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
449
vendor/github.com/asaskevich/govalidator/README.md
generated
vendored
Normal file
@@ -0,0 +1,449 @@
|
||||
govalidator
|
||||
===========
|
||||
[](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://godoc.org/github.com/asaskevich/govalidator) [](https://coveralls.io/r/asaskevich/govalidator?branch=master) [](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
|
||||
[](https://travis-ci.org/asaskevich/govalidator) [](https://goreportcard.com/report/github.com/asaskevich/govalidator) [](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
58
vendor/github.com/asaskevich/govalidator/arrays.go
generated
vendored
Normal 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
116
vendor/github.com/asaskevich/govalidator/arrays_test.go
generated
vendored
Normal 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
45
vendor/github.com/asaskevich/govalidator/converter.go
generated
vendored
Normal 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)
|
||||
}
|
||||
78
vendor/github.com/asaskevich/govalidator/converter_test.go
generated
vendored
Normal file
78
vendor/github.com/asaskevich/govalidator/converter_test.go
generated
vendored
Normal 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
36
vendor/github.com/asaskevich/govalidator/error.go
generated
vendored
Normal 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
29
vendor/github.com/asaskevich/govalidator/error_test.go
generated
vendored
Normal 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
94
vendor/github.com/asaskevich/govalidator/numerics.go
generated
vendored
Normal 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)
|
||||
}
|
||||
349
vendor/github.com/asaskevich/govalidator/numerics_test.go
generated
vendored
Normal file
349
vendor/github.com/asaskevich/govalidator/numerics_test.go
generated
vendored
Normal 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
90
vendor/github.com/asaskevich/govalidator/patterns.go
generated
vendored
Normal 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
616
vendor/github.com/asaskevich/govalidator/types.go
generated
vendored
Normal 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'ex‑Ré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
262
vendor/github.com/asaskevich/govalidator/utils.go
generated
vendored
Normal 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
501
vendor/github.com/asaskevich/govalidator/utils_test.go
generated
vendored
Normal 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">`, "<img alt="foo&bar">"},
|
||||
}
|
||||
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
1167
vendor/github.com/asaskevich/govalidator/validator.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3040
vendor/github.com/asaskevich/govalidator/validator_test.go
generated
vendored
Normal file
3040
vendor/github.com/asaskevich/govalidator/validator_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
vendor/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
Normal file
15
vendor/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
Normal 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
2
vendor/github.com/boj/redistore/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
|
||||
19
vendor/github.com/boj/redistore/LICENSE
generated
vendored
Normal file
19
vendor/github.com/boj/redistore/LICENSE
generated
vendored
Normal 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
52
vendor/github.com/boj/redistore/README.md
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# redistore
|
||||
|
||||
[](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
4
vendor/github.com/boj/redistore/doc.go
generated
vendored
Normal 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
358
vendor/github.com/boj/redistore/redistore.go
generated
vendored
Normal 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
404
vendor/github.com/boj/redistore/redistore_test.go
generated
vendored
Normal 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
1
vendor/github.com/boombuler/barcode/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vscode/
|
||||
21
vendor/github.com/boombuler/barcode/LICENSE
generated
vendored
Normal file
21
vendor/github.com/boombuler/barcode/LICENSE
generated
vendored
Normal 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
53
vendor/github.com/boombuler/barcode/README.md
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
[](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.
|
||||
94
vendor/github.com/boombuler/barcode/aztec/aztec_test.go
generated
vendored
Normal file
94
vendor/github.com/boombuler/barcode/aztec/aztec_test.go
generated
vendored
Normal 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
62
vendor/github.com/boombuler/barcode/aztec/azteccode.go
generated
vendored
Normal 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
268
vendor/github.com/boombuler/barcode/aztec/encoder.go
generated
vendored
Normal 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
|
||||
}
|
||||
58
vendor/github.com/boombuler/barcode/aztec/encoder_test.go
generated
vendored
Normal file
58
vendor/github.com/boombuler/barcode/aztec/encoder_test.go
generated
vendored
Normal 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")
|
||||
}
|
||||
61
vendor/github.com/boombuler/barcode/aztec/errorcorrection.go
generated
vendored
Normal file
61
vendor/github.com/boombuler/barcode/aztec/errorcorrection.go
generated
vendored
Normal 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
171
vendor/github.com/boombuler/barcode/aztec/highlevel.go
generated
vendored
Normal 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
|
||||
}
|
||||
132
vendor/github.com/boombuler/barcode/aztec/highlevel_test.go
generated
vendored
Normal file
132
vendor/github.com/boombuler/barcode/aztec/highlevel_test.go
generated
vendored
Normal 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
264
vendor/github.com/boombuler/barcode/aztec/state.go
generated
vendored
Normal 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
75
vendor/github.com/boombuler/barcode/aztec/token.go
generated
vendored
Normal 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
42
vendor/github.com/boombuler/barcode/barcode.go
generated
vendored
Normal 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
49
vendor/github.com/boombuler/barcode/codabar/encoder.go
generated
vendored
Normal 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
|
||||
}
|
||||
32
vendor/github.com/boombuler/barcode/codabar/encoder_test.go
generated
vendored
Normal file
32
vendor/github.com/boombuler/barcode/codabar/encoder_test.go
generated
vendored
Normal 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
203
vendor/github.com/boombuler/barcode/code128/encode.go
generated
vendored
Normal 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
|
||||
}
|
||||
131
vendor/github.com/boombuler/barcode/code128/encode_test.go
generated
vendored
Normal file
131
vendor/github.com/boombuler/barcode/code128/encode_test.go
generated
vendored
Normal 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
|
||||
}
|
||||
143
vendor/github.com/boombuler/barcode/code128/encodingtable.go
generated
vendored
Normal file
143
vendor/github.com/boombuler/barcode/code128/encodingtable.go
generated
vendored
Normal 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
152
vendor/github.com/boombuler/barcode/code39/encoder.go
generated
vendored
Normal 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
|
||||
}
|
||||
31
vendor/github.com/boombuler/barcode/code39/encoder_test.go
generated
vendored
Normal file
31
vendor/github.com/boombuler/barcode/code39/encoder_test.go
generated
vendored
Normal 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
131
vendor/github.com/boombuler/barcode/code93/encoder.go
generated
vendored
Normal 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 ' '
|
||||
}
|
||||
39
vendor/github.com/boombuler/barcode/code93/encoder_test.go
generated
vendored
Normal file
39
vendor/github.com/boombuler/barcode/code93/encoder_test.go
generated
vendored
Normal 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")
|
||||
}
|
||||
213
vendor/github.com/boombuler/barcode/datamatrix/codelayout.go
generated
vendored
Normal file
213
vendor/github.com/boombuler/barcode/datamatrix/codelayout.go
generated
vendored
Normal 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
|
||||
}
|
||||
73
vendor/github.com/boombuler/barcode/datamatrix/codesize.go
generated
vendored
Normal file
73
vendor/github.com/boombuler/barcode/datamatrix/codesize.go
generated
vendored
Normal 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
Reference in New Issue
Block a user