initial commits
commit
fa167b5757
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2023 Charles Iliya Krempeaux :: http://changelog.ca/
|
||||
|
||||
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.
|
|
@ -0,0 +1,37 @@
|
|||
# go-httpbearer
|
||||
|
||||
Package **httpbearer** provides tool to deal with HTTP bearer tokens, for the Go programming language.
|
||||
|
||||
## Online Documention
|
||||
|
||||
Online documentation, which includes examples, can be found at: http://godoc.org/sourcecode.social/reiver/go-httpbearer
|
||||
|
||||
[![GoDoc](https://godoc.org/sourcecode.social/reiver/go-httpbearer?status.svg)](https://godoc.org/sourcecode.social/reiver/go-httpbearer)
|
||||
|
||||
## Example
|
||||
|
||||
Here is an example:
|
||||
|
||||
```go
|
||||
import "sourcecode.social/reiver/go-httpbearer"
|
||||
|
||||
value := req.Header.Get("Authorization")
|
||||
|
||||
// ...
|
||||
|
||||
bearerToken, successful := httpbearer.Parse(value)
|
||||
if !successful {
|
||||
//@TODO: the value of the Authorization header was not a bearer token.
|
||||
return ErrNotBearerToken
|
||||
}
|
||||
```
|
||||
|
||||
If the Authorization header was:
|
||||
```
|
||||
Authorization: Bearer WW91IGFyZSBub3QgYSBkcm9wIGluIHRoZSBvY2Vhbi4gWW91IGFyZSB0aGUgZW50aXJlIG9jZWFuLCBpbiBhIGRyb3Au
|
||||
```
|
||||
|
||||
Then the value of `bearerToken` would be:
|
||||
```go
|
||||
"WW91IGFyZSBub3QgYSBkcm9wIGluIHRoZSBvY2Vhbi4gWW91IGFyZSB0aGUgZW50aXJlIG9jZWFuLCBpbiBhIGRyb3Au"
|
||||
```
|
|
@ -0,0 +1,180 @@
|
|||
package httpbearer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse parses the value of an HTTP "Authorization" header and returns a bearer token, if there was one.
|
||||
// If there was no bearer token in the HTTP "Authorization" header, then the returned value of ‘successful’ will be false.
|
||||
//
|
||||
// The full HTTP request header will look something like this:
|
||||
//
|
||||
// "Authorization: Bearer WW91IGFyZSBub3QgYSBkcm9wIGluIHRoZSBvY2Vhbi4gWW91IGFyZSB0aGUgZW50aXJlIG9jZWFuLCBpbiBhIGRyb3Au\r\n"
|
||||
//
|
||||
// This function expected to receive just the value. So, with our previous example, that would be:
|
||||
//
|
||||
// "Bearer WW91IGFyZSBub3QgYSBkcm9wIGluIHRoZSBvY2Vhbi4gWW91IGFyZSB0aGUgZW50aXJlIG9jZWFuLCBpbiBhIGRyb3Au"
|
||||
//
|
||||
// Note that HTTP headers allow for extra "\t" and " " to be put in any place where a "\t" or " " already is.
|
||||
//
|
||||
// So, for example, this:
|
||||
//
|
||||
// "Authorization: Bearer abcde12345\r\n"
|
||||
//
|
||||
// And these:
|
||||
//
|
||||
// "Authorization: Bearer abcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer abcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer abcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer abcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer abcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer\tabcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer\t\tabcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer\t\t\tabcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer\t\t\t\tabcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer\t\t\t\t\tabcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer\t\t\t\t\t\tabcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer \t \t abcde12345\r\n"
|
||||
//
|
||||
// Are all equivalent.
|
||||
//
|
||||
// Also note that HTTP headers also allow for the values to be broken up in multiple lines.
|
||||
// The rule is that if a "\r\n" is followed by a " " or "\t" the that next line is part of the previous line.
|
||||
//
|
||||
// So, for example, this:
|
||||
//
|
||||
// "Authorization: Bearer abcde12345\r\n"
|
||||
//
|
||||
// And these:
|
||||
//
|
||||
// "Authorization:\r\n Bearer abcde12345\r\n"
|
||||
//
|
||||
// "Authorization:\r\n\tBearer abcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer\r\n abcde12345\r\n"
|
||||
//
|
||||
// "Authorization: Bearer\r\n\tabcde12345\r\n"
|
||||
//
|
||||
// "Authorization:\r\n \r\n\t Bearer \r\n\t \t\t\t abcde12345\r\n"
|
||||
//
|
||||
// Are all equivalent.
|
||||
//
|
||||
// Parse deals with all of these, too.
|
||||
func Parse(value string) (bearerToken string, successful bool) {
|
||||
|
||||
// Although the first important thing we expected it the string "Bearer",
|
||||
// there could be zero or more of these characters.
|
||||
//
|
||||
// • "\t" i.e,. horizontal tab (␉)
|
||||
// • " " i.e., space (␠)
|
||||
// • "\r\n" i.e., carriage return (␍), line feed (␊)
|
||||
//
|
||||
// In IETF RFC822 LWSP-char is defined as a horizontal tab (␉) or space (␠).
|
||||
//
|
||||
// Technically "\r\n" should always be followed by a " " or a "\t".
|
||||
// But we don't have to worry about that here. As the parser for the request
|
||||
// already dealt with that.
|
||||
value = trimleft(value)
|
||||
|
||||
// The first important thing we should see it "Bearer".
|
||||
//
|
||||
//@TODO: should this be case insensitive?
|
||||
{
|
||||
const expected string = "Bearer"
|
||||
|
||||
if !strings.HasPrefix(value, expected) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
value = value[len(expected):]
|
||||
}
|
||||
|
||||
// The next thing we should see is one of these 3:
|
||||
//
|
||||
// • "\t" i.e,. horizontal tab (␉)
|
||||
// • " " i.e., space (␠)
|
||||
// • "\r\n" i.e., carriage return (␍), line feed (␊)
|
||||
//
|
||||
// In IETF RFC822 LWSP-char is defined as a horizontal tab (␉) or space (␠).
|
||||
//
|
||||
// Technically "\r\n" should always be followed by a " " or a "\t".
|
||||
// But we don't have to worry about that here. As the parser for the request
|
||||
// already dealt with that.
|
||||
//
|
||||
// Note that what we are doing here is safe, even if we are dealing with the UTF-8 Unicode encoding.
|
||||
{
|
||||
if len(value) <= 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
c0, value := value[0], value[1:]
|
||||
|
||||
switch c0 {
|
||||
case ' ','\t':
|
||||
// Nothing here. We got LWSP-char. So we will continue.
|
||||
case '\r':
|
||||
if len(value) <= 0 {
|
||||
return "", false
|
||||
}
|
||||
c1, value := value[0], value[1:]
|
||||
value = value[1:]
|
||||
if '\n' != c1 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Nothing else here. We got "\r\n". So we will continue.
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// There could be more of these characters:
|
||||
//
|
||||
// • "\t" i.e,. horizontal tab (␉)
|
||||
// • " " i.e., space (␠)
|
||||
// • "\r\n" i.e., carriage return (␍), line feed (␊)
|
||||
//
|
||||
// We will consume them (and ignore them) if they are there.
|
||||
value = trimleft(value)
|
||||
|
||||
// What should be left is the bearer token (with possibly some LWSP-chars or "\r\n" after it).
|
||||
//
|
||||
// Note that what we are doing here is safe, even if we are dealing with the UTF-8 Unicode encoding.
|
||||
{
|
||||
if len(value) <= 0 {
|
||||
return "", true
|
||||
}
|
||||
|
||||
for i,c := range value {
|
||||
switch c {
|
||||
case ' ','\t':
|
||||
return value[:i], true
|
||||
case '\r':
|
||||
if len(value) < i+2 {
|
||||
return value, true
|
||||
}
|
||||
|
||||
next := value[i+1]
|
||||
if '\n' != next {
|
||||
return value[:i+1], true
|
||||
}
|
||||
|
||||
return value[:i], true
|
||||
}
|
||||
}
|
||||
|
||||
return value, true
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,300 @@
|
|||
package httpbearer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Value string
|
||||
Expected string
|
||||
}{
|
||||
{
|
||||
Value: "Bearer ",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer 0",
|
||||
Expected: "0",
|
||||
},
|
||||
{
|
||||
Value: "Bearer 1",
|
||||
Expected: "1",
|
||||
},
|
||||
{
|
||||
Value: "Bearer a",
|
||||
Expected: "a",
|
||||
},
|
||||
{
|
||||
Value: "Bearer x",
|
||||
Expected: "x",
|
||||
},
|
||||
{
|
||||
Value: "Bearer B",
|
||||
Expected: "B",
|
||||
},
|
||||
{
|
||||
Value: "Bearer Z",
|
||||
Expected: "Z",
|
||||
},
|
||||
{
|
||||
Value: "Bearer ۴",
|
||||
Expected: "۴",
|
||||
},
|
||||
{
|
||||
Value: "Bearer ۵",
|
||||
Expected: "۵",
|
||||
},
|
||||
{
|
||||
Value: "Bearer 🙂",
|
||||
Expected: "🙂",
|
||||
},
|
||||
{
|
||||
Value: "Bearer 😈",
|
||||
Expected: "😈",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer a",
|
||||
Expected: "a",
|
||||
},
|
||||
{
|
||||
Value: "Bearer ab",
|
||||
Expected: "ab",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abc",
|
||||
Expected: "abc",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcd",
|
||||
Expected: "abcd",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcde",
|
||||
Expected: "abcde",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer a ",
|
||||
Expected: "a",
|
||||
},
|
||||
{
|
||||
Value: "Bearer ab ",
|
||||
Expected: "ab",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abc ",
|
||||
Expected: "abc",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcd ",
|
||||
Expected: "abcd",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcde ",
|
||||
Expected: "abcde",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer a\t",
|
||||
Expected: "a",
|
||||
},
|
||||
{
|
||||
Value: "Bearer ab\t",
|
||||
Expected: "ab",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abc\t",
|
||||
Expected: "abc",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcd\t",
|
||||
Expected: "abcd",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcde\t",
|
||||
Expected: "abcde",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer a\r\n ",
|
||||
Expected: "a",
|
||||
},
|
||||
{
|
||||
Value: "Bearer ab\r\n ",
|
||||
Expected: "ab",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abc\r\n ",
|
||||
Expected: "abc",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcd\r\n ",
|
||||
Expected: "abcd",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcde\r\n ",
|
||||
Expected: "abcde",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer a\r\n\t",
|
||||
Expected: "a",
|
||||
},
|
||||
{
|
||||
Value: "Bearer ab\r\n\t",
|
||||
Expected: "ab",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abc\r\n\t",
|
||||
Expected: "abc",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcd\r\n\t",
|
||||
Expected: "abcd",
|
||||
},
|
||||
{
|
||||
Value: "Bearer abcde\r\n\t",
|
||||
Expected: "abcde",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer XYZ123\r",
|
||||
Expected: "XYZ123\r",
|
||||
},
|
||||
{
|
||||
Value: "Bearer XYZ123\r ",
|
||||
Expected: "XYZ123\r",
|
||||
},
|
||||
{
|
||||
Value: "Bearer XYZ123\r\t",
|
||||
Expected: "XYZ123\r",
|
||||
},
|
||||
{
|
||||
Value: "Bearer XYZ123\r\r\n ",
|
||||
Expected: "XYZ123\r",
|
||||
},
|
||||
{
|
||||
Value: "Bearer XYZ123\r\r\n\t",
|
||||
Expected: "XYZ123\r",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Bearer\tawcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Bearer \tawcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Bearer\t awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Bearer\r\n\tawcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Bearer \r\n\t awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "Bearer awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj ",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Bearer awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj\t",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Bearer awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj\r\n\t ",
|
||||
Expected: "awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
actual, successful := Parse(test.Value)
|
||||
expected := test.Expected
|
||||
|
||||
if !successful {
|
||||
t.Errorf("For test #%d, expected to be able to parse HTTP bearer token successfully but actually didn't.", testNumber)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
continue
|
||||
}
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("For test #%d, the actual bearer token is not what was expected.", testNumber)
|
||||
t.Logf("EXPECTED: %q", expected)
|
||||
t.Logf("ACTUAL: %q", actual)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_fail(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Value string
|
||||
}{
|
||||
{
|
||||
Value: "",
|
||||
},
|
||||
{
|
||||
Value: "bearer awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Token awcQ.j/YQLCF-yhC0Dah@wCn_NJe3e[VwKv1gzj!!SP3PHtj",
|
||||
},
|
||||
{
|
||||
Value: "Bearer",
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
actual, successful := Parse(test.Value)
|
||||
|
||||
if successful {
|
||||
t.Errorf("For test #%d, expected parseing of HTTP bearer token to be unsuccessfully but actually was.", testNumber)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
continue
|
||||
}
|
||||
|
||||
if expected := ""; expected != actual {
|
||||
t.Errorf("For test #%d, expected result to be empty string but actually wasn't.", testNumber)
|
||||
t.Logf("EXPECTED: %q", expected)
|
||||
t.Logf("ACTUAL: %q", actual)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package httpbearer
|
||||
|
||||
// There could be more of these characters:
|
||||
//
|
||||
// • "\t" i.e,. horizontal tab (␉)
|
||||
// • " " i.e., space (␠)
|
||||
// • "\r\n" i.e., carriage return (␍), line feed (␊)
|
||||
//
|
||||
// We will consume them (and ignore them) if they are there.
|
||||
func trimleft(value string) string {
|
||||
|
||||
for {
|
||||
if len(value) <= 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
c0 := value[0]
|
||||
|
||||
switch c0 {
|
||||
case ' ','\t':
|
||||
value = value[1:]
|
||||
// Nothing else here. We got LWSP-char. So we will continue.
|
||||
case '\r':
|
||||
if len(value) < 2 {
|
||||
return value
|
||||
}
|
||||
c1 := value[1]
|
||||
if '\n' != c1 {
|
||||
return value
|
||||
}
|
||||
value = value[2:]
|
||||
// Nothing else here. We got "\r\n". So we will continue.
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package httpbearer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTrimLeft(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Value string
|
||||
Expected string
|
||||
}{
|
||||
{
|
||||
Value: "",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: " ",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: " ",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: " ",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: " ",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: " ",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "\t",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: "\t\t",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: "\t\t\t",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: "\t\t\t\t",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: "\t\t\t\t\t",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: "\r\n\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: "\r\n\r\n\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: "\r\n\r\n\r\n\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Value: "\r\n\r\n\r\n\r\n\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "\t \r\n \r\n\t \t\r\n \r\n\r\n",
|
||||
Expected: "",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: " ToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: " ToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: " ToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: " ToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: " ToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "\tToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: "\t\tToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: "\t\t\tToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: "\t\t\t\tToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: "\t\t\t\t\tToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "\r\nToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: "\r\n\r\nToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: "\r\n\r\n\r\nToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: "\r\n\r\n\r\n\r\nToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
{
|
||||
Value: "\r\n\r\n\r\n\r\n\r\nToK3n]]*i4HD2@)",
|
||||
Expected: "ToK3n]]*i4HD2@)",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: "\t \r\n \r\n\t \t\r\n \r\n\r\nToK3n]]*i4HD2@",
|
||||
Expected: "ToK3n]]*i4HD2@",
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
actual := trimleft(test.Value)
|
||||
expected := test.Expected
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("For test #%d, the actual left-trimmed value is not what was expected.", testNumber)
|
||||
t.Logf("EXPECTED: %q", expected)
|
||||
t.Logf("ACTUAL: %q", actual)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue