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