Cameradar 3.0.0: Uses ullaakut/nmap, runs faster, removed legacy code (#188)
Unit tests functional and coverage back to 100% Add more routes to dictionary, add more credentials, add default port 5554, rename cameradar logs ENV variable, improve unit test readability, remove tmp file
This commit is contained in:
committed by
GitHub
parent
878ca9f032
commit
5849898283
+9
@@ -0,0 +1,9 @@
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
*.test
|
||||
|
||||
*.out
|
||||
*.txt
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- tip
|
||||
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
|
||||
script:
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
[](https://travis-ci.org/leodido/go-urn) [](https://codecov.io/gh/leodido/go-urn) [](https://godoc.org/github.com/leodido/go-urn)
|
||||
|
||||
**A parser for URNs**.
|
||||
|
||||
> As seen on [RFC 2141](https://tools.ietf.org/html/rfc2141#ref-1).
|
||||
|
||||
[API documentation](https://godoc.org/github.com/leodido/go-urn).
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
go get github.com/leodido/go-urn
|
||||
```
|
||||
|
||||
## Performances
|
||||
|
||||
This implementation results to be really fast.
|
||||
|
||||
Usually below ½ microsecond on my machine<sup>[1](#mymachine)</sup>.
|
||||
|
||||
Notice it also performs, while parsing:
|
||||
|
||||
1. fine-grained and informative erroring
|
||||
2. specific-string normalization
|
||||
|
||||
```
|
||||
ok/00/urn:a:b______________________________________/-4 20000000 265 ns/op 182 B/op 6 allocs/op
|
||||
ok/01/URN:foo:a123,456_____________________________/-4 30000000 296 ns/op 200 B/op 6 allocs/op
|
||||
ok/02/urn:foo:a123%2c456___________________________/-4 20000000 331 ns/op 208 B/op 6 allocs/op
|
||||
ok/03/urn:ietf:params:scim:schemas:core:2.0:User___/-4 20000000 430 ns/op 280 B/op 6 allocs/op
|
||||
ok/04/urn:ietf:params:scim:schemas:extension:enterp/-4 20000000 411 ns/op 312 B/op 6 allocs/op
|
||||
ok/05/urn:ietf:params:scim:schemas:extension:enterp/-4 20000000 472 ns/op 344 B/op 6 allocs/op
|
||||
ok/06/urn:burnout:nss______________________________/-4 30000000 257 ns/op 192 B/op 6 allocs/op
|
||||
ok/07/urn:abcdefghilmnopqrstuvzabcdefghilm:x_______/-4 20000000 375 ns/op 213 B/op 6 allocs/op
|
||||
ok/08/urn:urnurnurn:urn____________________________/-4 30000000 265 ns/op 197 B/op 6 allocs/op
|
||||
ok/09/urn:ciao:@!=%2c(xyz)+a,b.*@g=$_'_____________/-4 20000000 307 ns/op 248 B/op 6 allocs/op
|
||||
ok/10/URN:x:abc%1dz%2f%3az_________________________/-4 30000000 259 ns/op 212 B/op 6 allocs/op
|
||||
no/11/URN:-xxx:x___________________________________/-4 20000000 445 ns/op 320 B/op 6 allocs/op
|
||||
no/12/urn::colon:nss_______________________________/-4 20000000 461 ns/op 320 B/op 6 allocs/op
|
||||
no/13/urn:abcdefghilmnopqrstuvzabcdefghilmn:specifi/-4 10000000 660 ns/op 320 B/op 6 allocs/op
|
||||
no/14/URN:a!?:x____________________________________/-4 20000000 507 ns/op 320 B/op 6 allocs/op
|
||||
no/15/urn:urn:NSS__________________________________/-4 20000000 429 ns/op 288 B/op 6 allocs/op
|
||||
no/16/urn:white_space:NSS__________________________/-4 20000000 482 ns/op 320 B/op 6 allocs/op
|
||||
no/17/urn:concat:no_spaces_________________________/-4 20000000 539 ns/op 328 B/op 7 allocs/op
|
||||
no/18/urn:a:/______________________________________/-4 20000000 470 ns/op 320 B/op 7 allocs/op
|
||||
no/19/urn:UrN:NSS__________________________________/-4 20000000 399 ns/op 288 B/op 6 allocs/op
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
* <a name="mymachine">[1]</a>: Intel Core i7-7600U CPU @ 2.80GHz
|
||||
|
||||
---
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
+1670
File diff suppressed because it is too large
Load Diff
+159
@@ -0,0 +1,159 @@
|
||||
package urn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]"
|
||||
errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col %d]"
|
||||
errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]"
|
||||
errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]"
|
||||
errHex = "expecting the specific string hex chars to be well-formed (%%alnum{2}) [col %d]"
|
||||
errParse = "parsing error [col %d]"
|
||||
)
|
||||
|
||||
%%{
|
||||
machine urn;
|
||||
|
||||
# unsigned alphabet
|
||||
alphtype uint8;
|
||||
|
||||
action mark {
|
||||
m.pb = m.p
|
||||
}
|
||||
|
||||
action tolower {
|
||||
m.tolower = append(m.tolower, m.p - m.pb)
|
||||
}
|
||||
|
||||
action set_pre {
|
||||
output.prefix = string(m.text())
|
||||
}
|
||||
|
||||
action set_nid {
|
||||
output.ID = string(m.text())
|
||||
}
|
||||
|
||||
action set_nss {
|
||||
raw := m.text()
|
||||
output.SS = string(raw)
|
||||
// Iterate upper letters lowering them
|
||||
for _, i := range m.tolower {
|
||||
raw[i] = raw[i] + 32
|
||||
}
|
||||
output.norm = string(raw)
|
||||
}
|
||||
|
||||
action err_pre {
|
||||
m.err = fmt.Errorf(errPrefix, m.p)
|
||||
fhold;
|
||||
fgoto fail;
|
||||
}
|
||||
|
||||
action err_nid {
|
||||
m.err = fmt.Errorf(errIdentifier, m.p)
|
||||
fhold;
|
||||
fgoto fail;
|
||||
}
|
||||
|
||||
action err_nss {
|
||||
m.err = fmt.Errorf(errSpecificString, m.p)
|
||||
fhold;
|
||||
fgoto fail;
|
||||
}
|
||||
|
||||
action err_urn {
|
||||
m.err = fmt.Errorf(errNoUrnWithinID, m.p)
|
||||
fhold;
|
||||
fgoto fail;
|
||||
}
|
||||
|
||||
action err_hex {
|
||||
m.err = fmt.Errorf(errHex, m.p)
|
||||
fhold;
|
||||
fgoto fail;
|
||||
}
|
||||
|
||||
action err_parse {
|
||||
m.err = fmt.Errorf(errParse, m.p)
|
||||
fhold;
|
||||
fgoto fail;
|
||||
}
|
||||
|
||||
pre = ([uU][rR][nN] @err(err_pre)) >mark %set_pre;
|
||||
|
||||
nid = (alnum >mark (alnum | '-'){0,31}) %set_nid;
|
||||
|
||||
hex = '%' (digit | lower | upper >tolower){2} $err(err_hex);
|
||||
|
||||
sss = (alnum | [()+,\-.:=@;$_!*']);
|
||||
|
||||
nss = (sss | hex)+ $err(err_nss);
|
||||
|
||||
fail := (any - [\n\r])* @err{ fgoto main; };
|
||||
|
||||
main := (pre ':' (nid - pre %err(err_urn)) $err(err_nid) ':' nss >mark %set_nss) $err(err_parse);
|
||||
|
||||
}%%
|
||||
|
||||
%% write data noerror noprefix;
|
||||
|
||||
// Machine is the interface representing the FSM
|
||||
type Machine interface {
|
||||
Error() error
|
||||
Parse(input []byte) (*URN, error)
|
||||
}
|
||||
|
||||
type machine struct {
|
||||
data []byte
|
||||
cs int
|
||||
p, pe, eof, pb int
|
||||
err error
|
||||
tolower []int
|
||||
}
|
||||
|
||||
// NewMachine creates a new FSM able to parse RFC 2141 strings.
|
||||
func NewMachine() Machine {
|
||||
m := &machine{}
|
||||
|
||||
%% access m.;
|
||||
%% variable p m.p;
|
||||
%% variable pe m.pe;
|
||||
%% variable eof m.eof;
|
||||
%% variable data m.data;
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Err returns the error that occurred on the last call to Parse.
|
||||
//
|
||||
// If the result is nil, then the line was parsed successfully.
|
||||
func (m *machine) Error() error {
|
||||
return m.err
|
||||
}
|
||||
|
||||
func (m *machine) text() []byte {
|
||||
return m.data[m.pb:m.p]
|
||||
}
|
||||
|
||||
// Parse parses the input byte array as a RFC 2141 string.
|
||||
func (m *machine) Parse(input []byte) (*URN, error) {
|
||||
m.data = input
|
||||
m.p = 0
|
||||
m.pb = 0
|
||||
m.pe = len(input)
|
||||
m.eof = len(input)
|
||||
m.err = nil
|
||||
m.tolower = []int{}
|
||||
output := &URN{}
|
||||
|
||||
%% write init;
|
||||
%% write exec;
|
||||
|
||||
if m.cs < first_final || m.cs == en_fail {
|
||||
return nil, m.err
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package urn
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
for ii, tt := range tests {
|
||||
urn, err := NewMachine().Parse([]byte(tt.in))
|
||||
ok := err == nil
|
||||
|
||||
if ok {
|
||||
assert.True(t, tt.ok, herror(ii, tt))
|
||||
assert.Equal(t, tt.obj.prefix, urn.prefix, herror(ii, tt))
|
||||
assert.Equal(t, tt.obj.ID, urn.ID, herror(ii, tt))
|
||||
assert.Equal(t, tt.obj.SS, urn.SS, herror(ii, tt))
|
||||
assert.Equal(t, tt.str, urn.String(), herror(ii, tt))
|
||||
assert.Equal(t, tt.norm, urn.Normalize().String(), herror(ii, tt))
|
||||
} else {
|
||||
assert.False(t, tt.ok, herror(ii, tt))
|
||||
assert.Empty(t, urn, herror(ii, tt))
|
||||
assert.Equal(t, tt.estr, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
SHELL := /bin/bash
|
||||
|
||||
machine.go: machine.go.rl
|
||||
ragel -Z -G2 -e -o $@ $<
|
||||
@gofmt -w -s $@
|
||||
@sed -i '/^\/\/line/d' $@
|
||||
|
||||
.PHONY: build
|
||||
build: machine.go
|
||||
|
||||
.PHONY: bench
|
||||
bench: *_test.go machine.go
|
||||
go test -bench=. -benchmem -benchtime=5s ./...
|
||||
|
||||
.PHONY: tests
|
||||
tests: *_test.go machine.go
|
||||
go test -race -timeout 10s -coverprofile=coverage.out -covermode=atomic -v ./...
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package urn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var benchs = []testCase{
|
||||
tests[14],
|
||||
tests[2],
|
||||
tests[6],
|
||||
tests[10],
|
||||
tests[11],
|
||||
tests[13],
|
||||
tests[20],
|
||||
tests[23],
|
||||
tests[33],
|
||||
tests[45],
|
||||
tests[47],
|
||||
tests[48],
|
||||
tests[50],
|
||||
tests[52],
|
||||
tests[53],
|
||||
tests[57],
|
||||
tests[62],
|
||||
tests[63],
|
||||
tests[67],
|
||||
tests[60],
|
||||
}
|
||||
|
||||
// This is here to avoid compiler optimizations that
|
||||
// could remove the actual call we are benchmarking
|
||||
// during benchmarks
|
||||
var benchParseResult *URN
|
||||
|
||||
func BenchmarkParse(b *testing.B) {
|
||||
for ii, tt := range benchs {
|
||||
tt := tt
|
||||
outcome := (map[bool]string{true: "ok", false: "no"})[tt.ok]
|
||||
b.Run(
|
||||
fmt.Sprintf("%s/%02d/%s/", outcome, ii, rxpad(string(tt.in), 45)),
|
||||
func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchParseResult, _ = Parse(tt.in)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
+899
@@ -0,0 +1,899 @@
|
||||
package urn
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ierror(index int) string {
|
||||
return "Test case num. " + strconv.Itoa(index+1)
|
||||
}
|
||||
|
||||
func herror(index int, test testCase) string {
|
||||
return ierror(index) + ", input \"" + string(test.in) + "\""
|
||||
}
|
||||
|
||||
func rxpad(str string, lim int) string {
|
||||
str = str + strings.Repeat(" ", lim)
|
||||
return str[:lim]
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
in []byte // the input
|
||||
ok bool // whether it is valid or not
|
||||
obj *URN // a pointer to the resulting urn.URN instance
|
||||
str string // string representation
|
||||
norm string // norm string representation
|
||||
estr string // error string
|
||||
}
|
||||
|
||||
var tests = []testCase{
|
||||
// ok
|
||||
{
|
||||
[]byte("urn:simple:simple"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "simple",
|
||||
SS: "simple",
|
||||
},
|
||||
"urn:simple:simple",
|
||||
"urn:simple:simple",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ciao:%5D"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: "%5D",
|
||||
},
|
||||
"urn:ciao:%5D",
|
||||
"urn:ciao:%5d",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - RFC examples
|
||||
{
|
||||
[]byte("URN:foo:a123,456"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "foo",
|
||||
SS: "a123,456",
|
||||
},
|
||||
"URN:foo:a123,456",
|
||||
"urn:foo:a123,456",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:foo:a123,456"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "foo",
|
||||
SS: "a123,456",
|
||||
},
|
||||
"urn:foo:a123,456",
|
||||
"urn:foo:a123,456",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:FOO:a123,456"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "FOO",
|
||||
SS: "a123,456",
|
||||
},
|
||||
"urn:FOO:a123,456",
|
||||
"urn:foo:a123,456",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:foo:A123,456"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "foo",
|
||||
SS: "A123,456",
|
||||
},
|
||||
"urn:foo:A123,456",
|
||||
"urn:foo:A123,456",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:foo:a123%2C456"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "foo",
|
||||
SS: "a123%2C456",
|
||||
},
|
||||
"urn:foo:a123%2C456",
|
||||
"urn:foo:a123%2c456",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:FOO:a123%2c456"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "FOO",
|
||||
SS: "a123%2c456",
|
||||
},
|
||||
"URN:FOO:a123%2c456",
|
||||
"urn:foo:a123%2c456",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:FOO:ABC%FFabc123%2c456"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "FOO",
|
||||
SS: "ABC%FFabc123%2c456",
|
||||
},
|
||||
"URN:FOO:ABC%FFabc123%2c456",
|
||||
"urn:foo:ABC%ffabc123%2c456",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:FOO:ABC%FFabc123%2C456%9A"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "FOO",
|
||||
SS: "ABC%FFabc123%2C456%9A",
|
||||
},
|
||||
"URN:FOO:ABC%FFabc123%2C456%9A",
|
||||
"urn:foo:ABC%ffabc123%2c456%9a",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - SCIM v2
|
||||
{
|
||||
[]byte("urn:ietf:params:scim:schemas:core:2.0:User"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ietf",
|
||||
SS: "params:scim:schemas:core:2.0:User",
|
||||
},
|
||||
"urn:ietf:params:scim:schemas:core:2.0:User",
|
||||
"urn:ietf:params:scim:schemas:core:2.0:User",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ietf",
|
||||
SS: "params:scim:schemas:extension:enterprise:2.0:User",
|
||||
},
|
||||
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
|
||||
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:userName"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ietf",
|
||||
SS: "params:scim:schemas:extension:enterprise:2.0:User:userName",
|
||||
},
|
||||
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:userName",
|
||||
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:userName",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:meta.lastModified"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ietf",
|
||||
SS: "params:scim:schemas:extension:enterprise:2.0:User:meta.lastModified",
|
||||
},
|
||||
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:meta.lastModified",
|
||||
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:meta.lastModified",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - minimum urn
|
||||
{
|
||||
[]byte("urn:a:b"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "a",
|
||||
SS: "b",
|
||||
},
|
||||
"urn:a:b",
|
||||
"urn:a:b",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:a::"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "a",
|
||||
SS: ":",
|
||||
},
|
||||
"urn:a::",
|
||||
"urn:a::",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:a:-"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "a",
|
||||
SS: "-",
|
||||
},
|
||||
"urn:a:-",
|
||||
"urn:a:-",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - URN prefix is case-insensitive
|
||||
{
|
||||
[]byte("URN:simple:simple"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "simple",
|
||||
SS: "simple",
|
||||
},
|
||||
"URN:simple:simple",
|
||||
"urn:simple:simple",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("Urn:simple:simple"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "Urn",
|
||||
ID: "simple",
|
||||
SS: "simple",
|
||||
},
|
||||
"Urn:simple:simple",
|
||||
"urn:simple:simple",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - ID can contain the "urn" string but it can not be exactly equal to it
|
||||
{
|
||||
[]byte("urn:urna:simple"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "urna",
|
||||
SS: "simple",
|
||||
},
|
||||
"urn:urna:simple",
|
||||
"urn:urna:simple",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:burnout:nss"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "burnout",
|
||||
SS: "nss",
|
||||
},
|
||||
"urn:burnout:nss",
|
||||
"urn:burnout:nss",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:burn:nss"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "burn",
|
||||
SS: "nss",
|
||||
},
|
||||
"urn:burn:nss",
|
||||
"urn:burn:nss",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:urnurnurn:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "urnurnurn",
|
||||
SS: "x",
|
||||
},
|
||||
"urn:urnurnurn:x",
|
||||
"urn:urnurnurn:x",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - ID can contains maximum 32 characters
|
||||
{
|
||||
[]byte("urn:abcdefghilmnopqrstuvzabcdefghilm:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "abcdefghilmnopqrstuvzabcdefghilm",
|
||||
SS: "x",
|
||||
},
|
||||
"urn:abcdefghilmnopqrstuvzabcdefghilm:x",
|
||||
"urn:abcdefghilmnopqrstuvzabcdefghilm:x",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - ID can be alpha numeric
|
||||
{
|
||||
[]byte("URN:123:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "123",
|
||||
SS: "x",
|
||||
},
|
||||
"URN:123:x",
|
||||
"urn:123:x",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:1ab:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "1ab",
|
||||
SS: "x",
|
||||
},
|
||||
"URN:1ab:x",
|
||||
"urn:1ab:x",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:a1b:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "a1b",
|
||||
SS: "x",
|
||||
},
|
||||
"URN:a1b:x",
|
||||
"urn:a1b:x",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:a12:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "a12",
|
||||
SS: "x",
|
||||
},
|
||||
"URN:a12:x",
|
||||
"urn:a12:x",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:cd2:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "cd2",
|
||||
SS: "x",
|
||||
},
|
||||
"URN:cd2:x",
|
||||
"urn:cd2:x",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - ID can contain an hyphen (not in its first position, see below)
|
||||
{
|
||||
[]byte("URN:abcd-:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "abcd-",
|
||||
SS: "x",
|
||||
},
|
||||
"URN:abcd-:x",
|
||||
"urn:abcd-:x",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:abcd-abcd:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "abcd-abcd",
|
||||
SS: "x",
|
||||
},
|
||||
"URN:abcd-abcd:x",
|
||||
"urn:abcd-abcd:x",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("URN:a123-456z:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "a123-456z",
|
||||
SS: "x",
|
||||
},
|
||||
"URN:a123-456z:x",
|
||||
"urn:a123-456z:x",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - SS can contain the "urn" string, also be exactly equal to it
|
||||
{
|
||||
[]byte("urn:urnx:urn"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "urnx",
|
||||
SS: "urn",
|
||||
},
|
||||
"urn:urnx:urn",
|
||||
"urn:urnx:urn",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:urnurnurn:urn"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "urnurnurn",
|
||||
SS: "urn",
|
||||
},
|
||||
"urn:urnurnurn:urn",
|
||||
"urn:urnurnurn:urn",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:hey:urnurnurn"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "hey",
|
||||
SS: "urnurnurn",
|
||||
},
|
||||
"urn:hey:urnurnurn",
|
||||
"urn:hey:urnurnurn",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - SS can contains and discerns multiple colons, also at the end
|
||||
{
|
||||
[]byte("urn:ciao:a:b:c"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: "a:b:c",
|
||||
},
|
||||
"urn:ciao:a:b:c",
|
||||
"urn:ciao:a:b:c",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:aaa:x:y:"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "aaa",
|
||||
SS: "x:y:",
|
||||
},
|
||||
"urn:aaa:x:y:",
|
||||
"urn:aaa:x:y:",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:aaa:x:y:"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "aaa",
|
||||
SS: "x:y:",
|
||||
},
|
||||
"urn:aaa:x:y:",
|
||||
"urn:aaa:x:y:",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - SS can contain (and also start with) some non-alphabetical (ie., OTHER) characters
|
||||
{
|
||||
[]byte("urn:ciao:-"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: "-",
|
||||
},
|
||||
"urn:ciao:-",
|
||||
"urn:ciao:-",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ciao::"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: ":",
|
||||
},
|
||||
"urn:ciao::",
|
||||
"urn:ciao::",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:colon:::::nss"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "colon",
|
||||
SS: "::::nss",
|
||||
},
|
||||
"urn:colon:::::nss",
|
||||
"urn:colon:::::nss",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ciao:!"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: "!",
|
||||
},
|
||||
"urn:ciao:!",
|
||||
"urn:ciao:!",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ciao:!!*"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: "!!*",
|
||||
},
|
||||
"urn:ciao:!!*",
|
||||
"urn:ciao:!!*",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ciao:-!:-,:x"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: "-!:-,:x",
|
||||
},
|
||||
"urn:ciao:-!:-,:x",
|
||||
"urn:ciao:-!:-,:x",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ciao:=@"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: "=@",
|
||||
},
|
||||
"urn:ciao:=@",
|
||||
"urn:ciao:=@",
|
||||
"",
|
||||
},
|
||||
{
|
||||
[]byte("urn:ciao:@!=%2C(xyz)+a,b.*@g=$_'"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "urn",
|
||||
ID: "ciao",
|
||||
SS: "@!=%2C(xyz)+a,b.*@g=$_'",
|
||||
},
|
||||
"urn:ciao:@!=%2C(xyz)+a,b.*@g=$_'",
|
||||
"urn:ciao:@!=%2c(xyz)+a,b.*@g=$_'",
|
||||
"",
|
||||
},
|
||||
|
||||
// ok - SS can contain (and also start with) hexadecimal representation of octets
|
||||
{
|
||||
[]byte("URN:hexes:%25"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "hexes",
|
||||
SS: "%25",
|
||||
},
|
||||
"URN:hexes:%25",
|
||||
"urn:hexes:%25",
|
||||
"",
|
||||
}, // Literal use of the "%" character in a namespace must be encoded using "%25"
|
||||
{
|
||||
[]byte("URN:x:abc%1Dz%2F%3az"),
|
||||
true,
|
||||
&URN{
|
||||
prefix: "URN",
|
||||
ID: "x",
|
||||
SS: "abc%1Dz%2F%3az",
|
||||
},
|
||||
"URN:x:abc%1Dz%2F%3az",
|
||||
"urn:x:abc%1dz%2f%3az",
|
||||
"",
|
||||
}, // Literal use of the "%" character in a namespace must be encoded using "%25"
|
||||
|
||||
// no - ID can not start with an hyphen
|
||||
{
|
||||
[]byte("URN:-xxx:x"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 4]`,
|
||||
},
|
||||
{
|
||||
[]byte("URN:---xxx:x"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 4]`,
|
||||
},
|
||||
|
||||
// no - ID can not start with a colon
|
||||
{
|
||||
[]byte("urn::colon:nss"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 4]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn::::nss"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 4]`,
|
||||
},
|
||||
|
||||
// no - ID can not contains more than 32 characters
|
||||
{
|
||||
[]byte("urn:abcdefghilmnopqrstuvzabcdefghilmn:specificstring"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 36]`,
|
||||
},
|
||||
|
||||
// no - ID can not contain special characters
|
||||
{
|
||||
[]byte("URN:a!?:x"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 5]`,
|
||||
},
|
||||
{
|
||||
[]byte("URN:@,:x"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 4]`,
|
||||
},
|
||||
{
|
||||
[]byte("URN:#,:x"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 4]`,
|
||||
},
|
||||
{
|
||||
[]byte("URN:bc'.@:x"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 6]`,
|
||||
},
|
||||
|
||||
// no - ID can not be equal to "urn"
|
||||
{
|
||||
[]byte("urn:urn:NSS"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to not contain the "urn" reserved string [col 7]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn:URN:NSS"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to not contain the "urn" reserved string [col 7]`,
|
||||
},
|
||||
{
|
||||
[]byte("URN:URN:NSS"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to not contain the "urn" reserved string [col 7]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn:UrN:NSS"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to not contain the "urn" reserved string [col 7]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn:Urn:NSS"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to not contain the "urn" reserved string [col 7]`,
|
||||
},
|
||||
|
||||
// no - ID can not contain spaces
|
||||
{
|
||||
[]byte("urn:white space:NSS"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 9]`,
|
||||
},
|
||||
|
||||
// no - SS can not contain spaces
|
||||
{
|
||||
[]byte("urn:concat:no spaces"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col 13]`,
|
||||
},
|
||||
|
||||
// no - SS can not contain reserved characters (can accept them only if %-escaped)
|
||||
{
|
||||
[]byte("urn:a:%"), // the presence of an "%" character in an URN MUST be followed by two characters from the <hex> character set
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the specific string hex chars to be well-formed (%alnum{2}) [col 7]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn:a:?"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col 6]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn:a:#"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col 6]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn:a:/"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col 6]`,
|
||||
},
|
||||
|
||||
// no - Incomplete URNs
|
||||
{
|
||||
[]byte("urn:"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 4]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn::"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col 4]`,
|
||||
},
|
||||
{
|
||||
[]byte("urn:a:"),
|
||||
false,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
`expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col 6]`,
|
||||
},
|
||||
// {
|
||||
// "urn:a",
|
||||
// false,
|
||||
// nil,
|
||||
// "",
|
||||
// "",
|
||||
// "",
|
||||
// },
|
||||
}
|
||||
|
||||
var equivalenceTests = []struct {
|
||||
eq bool
|
||||
lx []byte
|
||||
rx []byte
|
||||
}{
|
||||
{
|
||||
true,
|
||||
[]byte("urn:foo:a123%2C456"),
|
||||
[]byte("URN:FOO:a123%2c456"),
|
||||
},
|
||||
{
|
||||
true,
|
||||
[]byte("urn:foo:AbC123%2C456"),
|
||||
[]byte("URN:FOO:AbC123%2c456"),
|
||||
},
|
||||
{
|
||||
true,
|
||||
[]byte("urn:foo:AbC123%2C456%1f"),
|
||||
[]byte("URN:FOO:AbC123%2c456%1f"),
|
||||
},
|
||||
{
|
||||
true,
|
||||
[]byte("URN:foo:a123,456"),
|
||||
[]byte("urn:foo:a123,456"),
|
||||
},
|
||||
{
|
||||
true,
|
||||
[]byte("URN:foo:a123,456"),
|
||||
[]byte("urn:FOO:a123,456"),
|
||||
},
|
||||
{
|
||||
true,
|
||||
[]byte("urn:foo:a123,456"),
|
||||
[]byte("urn:FOO:a123,456"),
|
||||
},
|
||||
{
|
||||
true,
|
||||
[]byte("urn:ciao:%2E"),
|
||||
[]byte("urn:ciao:%2e"),
|
||||
},
|
||||
{
|
||||
false,
|
||||
[]byte("urn:foo:A123,456"),
|
||||
[]byte("URN:foo:a123,456"),
|
||||
},
|
||||
{
|
||||
false,
|
||||
[]byte("urn:foo:A123,456"),
|
||||
[]byte("urn:foo:a123,456"),
|
||||
},
|
||||
{
|
||||
false,
|
||||
[]byte("urn:foo:A123,456"),
|
||||
[]byte("urn:FOO:a123,456"),
|
||||
},
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package urn
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// URN represents an Uniform Resource Name.
|
||||
//
|
||||
// The general form represented is:
|
||||
//
|
||||
// urn:<id>:<ss>
|
||||
//
|
||||
// Details at https://tools.ietf.org/html/rfc2141.
|
||||
type URN struct {
|
||||
prefix string // Static prefix. Equal to "urn" when empty.
|
||||
ID string // Namespace identifier
|
||||
SS string // Namespace specific string
|
||||
norm string // Normalized namespace specific string
|
||||
}
|
||||
|
||||
// Normalize turns the receiving URN into its norm version.
|
||||
//
|
||||
// Which means: lowercase prefix, lowercase namespace identifier, and immutate namespace specific string chars (except <hex> tokens which are lowercased).
|
||||
func (u *URN) Normalize() *URN {
|
||||
return &URN{
|
||||
prefix: "urn",
|
||||
ID: strings.ToLower(u.ID),
|
||||
SS: u.norm,
|
||||
}
|
||||
}
|
||||
|
||||
// Equal checks the lexical equivalence of the current URN with another one.
|
||||
func (u *URN) Equal(x *URN) bool {
|
||||
return *u.Normalize() == *x.Normalize()
|
||||
}
|
||||
|
||||
// String reassembles the URN into a valid URN string.
|
||||
//
|
||||
// This requires both ID and SS fields to be non-empty.
|
||||
// Otherwise it returns an empty string.
|
||||
//
|
||||
// Default URN prefix is "urn".
|
||||
func (u *URN) String() string {
|
||||
var res string
|
||||
if u.ID != "" && u.SS != "" {
|
||||
if u.prefix == "" {
|
||||
res += "urn"
|
||||
}
|
||||
res += u.prefix + ":" + u.ID + ":" + u.SS
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Parse is responsible to create an URN instance from a byte array matching the correct URN syntax.
|
||||
func Parse(u []byte) (*URN, bool) {
|
||||
urn, err := NewMachine().Parse(u)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return urn, true
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package urn
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDefaultPrefixWhenString(t *testing.T) {
|
||||
u := &URN{
|
||||
ID: "pippo",
|
||||
SS: "pluto",
|
||||
}
|
||||
|
||||
assert.Equal(t, "urn:pippo:pluto", u.String())
|
||||
}
|
||||
|
||||
func TestParseSignature(t *testing.T) {
|
||||
urn, ok := Parse([]byte(``))
|
||||
assert.Nil(t, urn)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestLexicalEquivalence(t *testing.T) {
|
||||
for ii, tt := range equivalenceTests {
|
||||
urnlx, oklx := Parse(tt.lx)
|
||||
urnrx, okrx := Parse(tt.rx)
|
||||
|
||||
if oklx && okrx {
|
||||
|
||||
assert.True(t, urnlx.Equal(urnlx))
|
||||
assert.True(t, urnrx.Equal(urnrx))
|
||||
|
||||
if tt.eq {
|
||||
assert.True(t, urnlx.Equal(urnrx), ierror(ii))
|
||||
assert.True(t, urnrx.Equal(urnlx), ierror(ii))
|
||||
} else {
|
||||
assert.False(t, urnlx.Equal(urnrx), ierror(ii))
|
||||
assert.False(t, urnrx.Equal(urnlx), ierror(ii))
|
||||
}
|
||||
} else {
|
||||
t.Log("Something wrong in the testing table ...")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user