pax_global_header00006660000000000000000000000064145677265400014532gustar00rootroot0000000000000052 comment=475331c3058e831ee3d0083e169ba988b7bd74f1 golang-github-muhlemmer-httpforwarded-0.1.0/000077500000000000000000000000001456772654000211235ustar00rootroot00000000000000golang-github-muhlemmer-httpforwarded-0.1.0/.github/000077500000000000000000000000001456772654000224635ustar00rootroot00000000000000golang-github-muhlemmer-httpforwarded-0.1.0/.github/workflows/000077500000000000000000000000001456772654000245205ustar00rootroot00000000000000golang-github-muhlemmer-httpforwarded-0.1.0/.github/workflows/go.yml000066400000000000000000000011131456772654000256440ustar00rootroot00000000000000name: Go on: push: branches: [ "master" ] pull_request: branches: [ "master" ] jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: go: ['1.19', '1.20', '1.21'] name: Go ${{ matrix.go }} test steps: - uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v4 with: go-version: ${{ matrix.go }} - name: Test run: go test -race -v -coverprofile=profile.cov ./... - uses: codecov/codecov-action@v3 with: file: ./profile.cov name: codecov-go golang-github-muhlemmer-httpforwarded-0.1.0/.gitignore000066400000000000000000000004121456772654000231100ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof golang-github-muhlemmer-httpforwarded-0.1.0/LICENSE000066400000000000000000000027741456772654000221420ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. Copyright (c) 2016 Tim Heckman. 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 Google Inc. 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 OWNER 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. golang-github-muhlemmer-httpforwarded-0.1.0/README.md000066400000000000000000000042711456772654000224060ustar00rootroot00000000000000# httpforwarded [![License](https://img.shields.io/badge/license-BSD--style_3--clause-brightgreen.svg?style=flat)](https://github.com/muhlemmer/httpforwarded/blob/master/LICENSE) [![Go Reference](https://pkg.go.dev/badge/github.com/muhlemmer/httpforwarded.svg)](https://pkg.go.dev/github.com/muhlemmer/httpforwarded) [![Go test](https://github.com/muhlemmer/httpforwarded/actions/workflows/go.yml/badge.svg)](https://github.com/muhlemmer/httpforwarded/actions/workflows/go.yml) [![codecov](https://codecov.io/gh/muhlemmer/httpforwarded/graph/badge.svg?token=E3G7W40GLU)](https://codecov.io/gh/muhlemmer/httpforwarded) The `httpforwarded` go package provides utility functions for working with the `Forwarded` HTTP header as defined in [RFC-7239](https://tools.ietf.org/html/rfc7239). This header is proposed to replace the `X-Forwarded-For` and `X-Forwarded-Proto` headers, amongst others. This package was heavily inspired by the `mime` package in the standard library, more specifically the [ParseMediaType()](https://golang.org/pkg/mime/#ParseMediaType) function. This is a **fork** from https://github.com/theckman/httpforwarded, which seems to have become idle for several years. ## License This package copies some functions, without modification, from the Go standard library. As such, the entirety of this package is released under the same permissive BSD-style license as the Go language itself. Please see the contents of the [LICENSE](https://github.com/muhlemmer/httpforwarded/blob/master/LICENSE) file for the full details of the license. ## Installing To install this package for consumption, you can run the following: ``` go get -u github.com/muhlemmer/httpforwarded ``` ## Usage Given a `*http.Request`: ```Go // var req *http.Request params, _ := httpforwarded.ParseFromRequest(req) // you can then do something like this to get the first "for" param: fmt.Printf("origin %s", params["for"][0]) ``` Given a list of `Forwarded` header values: ```Go // var req *http.Request headerValues := req.Header[http.CanonicalHeaderKey("forwarded")] params, _ := httpforwarded.Parse(headerValues) // you can then do something like this to get the first "for" param: fmt.Printf("origin %s", params["for"][0]) ``` golang-github-muhlemmer-httpforwarded-0.1.0/doc.go000066400000000000000000000015451456772654000222240ustar00rootroot00000000000000// Copyright 2016 Tim Heckman. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package httpforwarded is a helper package for parsing the Forwarded HTTP // header as defined in RFC-7239 [1]. There is a function for parsing the value // of multiple Forwarded headers, and a function for formatting a Forwarded // header. // // Because an HTTP request can have multiple Forwarded headers with unique // fields, we need to support parsing and joining of all of them in the order // they are provided. // // Here is an example of parsing the Forwarded header: // // // var req *http.Request // vals := req.Header[http.CanonicalHeaderKey("forwarded")] // get all Forwarded fields // params, _ := httpforwarded.Parse(vals) // // [1] https://tools.ietf.org/html/rfc7239 package httpforwarded golang-github-muhlemmer-httpforwarded-0.1.0/example_test.go000066400000000000000000000023571456772654000241530ustar00rootroot00000000000000// Copyright 2016 Tim Heckman. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package httpforwarded_test import ( "fmt" "github.com/muhlemmer/httpforwarded" ) func ExampleParse() { // mock value of HTTP headers headers := []string{"for=192.0.2.1; proto=http"} // parse the fields in to one map params, _ := httpforwarded.Parse(headers) // print the origin IP address and protocolg fmt.Printf("origin: %s | protocol: %s", params["for"][0], params["proto"][0]) // output: origin: 192.0.2.1 | protocol: http } func ExampleFormat() { // build a parameter map params := map[string][]string{ "for": []string{"192.0.2.1", "192.0.2.4"}, "proto": []string{"http"}, } // format the parameter map val := httpforwarded.Format(params) fmt.Print(val) // output: for=192.0.2.1, for=192.0.2.4; proto=http } func ExampleParseParameter() { // mock value of HTTP headers headers := []string{"for=192.0.2.1, for=192.0.2.42; proto=http"} // parse the header fields while only extracting 'for' parameter values, _ := httpforwarded.ParseParameter("for", headers) // print the slice of values fmt.Printf("%#v", values) // output: []string{"192.0.2.1", "192.0.2.42"} } golang-github-muhlemmer-httpforwarded-0.1.0/format.go000066400000000000000000000032041456772654000227410ustar00rootroot00000000000000// Copyright 2016 Tim Heckman. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package httpforwarded import ( "bytes" "sort" "strings" "sync" ) var bytesBufferPool = &sync.Pool{ New: func() interface{} { return &bytes.Buffer{} }, } // Format is a function that takes a map[string][]string and generates the // appropriate content for an HTTP Forwarded header. func Format(params map[string][]string) string { if len(params) == 0 { return "" } buf := bytesBufferPool.Get().(*bytes.Buffer) buf.Reset() for paramCount, paramName := range sortedParamKeys(params) { values := params[paramName] for i, val := range values { // write key= buf.WriteString(paramName) buf.WriteByte('=') // if this is a token, write the value without wrapping in quotes if strings.IndexFunc(val, isNotTokenChar) == -1 { buf.WriteString(val) } else { // wrap the value in quotes buf.WriteByte('"') buf.WriteString(val) buf.WriteByte('"') } // if this is not the last value of this parameter // write a comma and a space if len(values) > i+1 { buf.WriteString(", ") } else { // if this is not the last parameter // write a semi-colon and a space if len(params) > paramCount+1 { buf.WriteString("; ") } } } } out := buf.String() bytesBufferPool.Put(buf) return out } func sortedParamKeys(params map[string][]string) []string { keys := make(sort.StringSlice, len(params)) var keyCount int for paramName := range params { keys[keyCount] = paramName keyCount++ } sort.Sort(keys) return keys } golang-github-muhlemmer-httpforwarded-0.1.0/format_test.go000066400000000000000000000012371456772654000240040ustar00rootroot00000000000000// Copyright 2016 Tim Heckman. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package httpforwarded_test import ( "github.com/muhlemmer/httpforwarded" . "gopkg.in/check.v1" ) func (*TestSuite) TestFormat(c *C) { var out string out = httpforwarded.Format(nil) c.Check(out, Equals, "") params := map[string][]string{ "for": []string{"192.0.2.1", "192.0.2.4"}, "by": []string{"192.0.2.200", "192.0.2.202"}, "proto": []string{"http"}, } out = httpforwarded.Format(params) c.Check(out, Equals, "by=192.0.2.200, by=192.0.2.202; for=192.0.2.1, for=192.0.2.4; proto=http") } golang-github-muhlemmer-httpforwarded-0.1.0/go.mod000066400000000000000000000003161456772654000222310ustar00rootroot00000000000000module github.com/muhlemmer/httpforwarded go 1.19 require gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c require ( github.com/kr/pretty v0.2.1 // indirect github.com/kr/text v0.1.0 // indirect ) golang-github-muhlemmer-httpforwarded-0.1.0/go.sum000066400000000000000000000011331456772654000222540ustar00rootroot00000000000000github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= golang-github-muhlemmer-httpforwarded-0.1.0/httpforwarded_test.go000066400000000000000000000005061456772654000253670ustar00rootroot00000000000000// Copyright 2016 Tim Heckman. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package httpforwarded_test import ( "testing" . "gopkg.in/check.v1" ) type TestSuite struct{} var _ = Suite(&TestSuite{}) func Test(t *testing.T) { TestingT(t) } golang-github-muhlemmer-httpforwarded-0.1.0/parse.go000066400000000000000000000133441456772654000225710ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Copyright 2016 Tim Heckman. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package httpforwarded import ( "bytes" "errors" "net/http" "strings" "unicode" ) // Parse is a helper function for parsing the HTTP Forwarded header as defined // in RFC-7239. There can be multiple Forwarded entries within a single HTTP // request, so this function takes a slice of strings. The parse function takes // care to preserve the order of parameter value as it was seen. Some of the // parameters may have multiple values. // // This function was inspired by the mime.ParseMediaType() function in the // Go stdlib. func Parse(values []string) (map[string][]string, error) { if len(values) == 0 { return nil, nil } params := make(map[string][]string) for _, v := range values { for len(v) > 0 { // trim any leading whitespace v = strings.TrimLeftFunc(v, unicode.IsSpace) if len(v) == 0 { break } // consume a key=value pair from the value key, value, rest := consumeForwardedParam(v) if key == "" { if strings.TrimSpace(rest) == ";" { // Ignore trailing semicolons. // Not an error. return params, nil } // Parse error. return nil, errors.New("forwarded: invalid parameter") } if _, exists := params[key]; exists { params[key] = append(params[key], value) } else { // allocate new slice with a len of 1 and a cap of 3 // then set the one value we have params[key] = make([]string, 1, 3) params[key][0] = value } v = rest } } return params, nil } // ParseFromRequest is a helper function that is identical to Parse, except that // it automatically extracts the "Forwarded" header values from the *http.Request. func ParseFromRequest(req *http.Request) (map[string][]string, error) { headerValues := req.Header[http.CanonicalHeaderKey("forwarded")] return Parse(headerValues) } // ParseParameter parses the Forwarded header values and returns a slice of // parameter values. The paramName parameter should be a lowercase string. func ParseParameter(paramName string, values []string) ([]string, error) { if paramName == "" { return nil, errors.New(`paramName must not be ""`) } if len(values) == 0 { return nil, nil } paramValues := make([]string, 0, 2) for _, v := range values { for len(v) > 0 { // trim any leading whitespace v = strings.TrimLeftFunc(v, unicode.IsSpace) if len(v) == 0 { break } // consume a key-value pair from the value key, value, rest := consumeForwardedParam(v) if key == "" { if strings.TrimSpace(rest) == ";" { // Ignore trailing semicolons. // Not an error. return paramValues, nil } // Parse error. return nil, errors.New("forwarded: invalid parameter") } v = rest // if this isn't the key we are looking for, move on if key != paramName { continue } paramValues = append(paramValues, value) } } return paramValues, nil } func consumeForwardedParam(v string) (param, value, rest string) { // trim any preliminary whitespace rest = strings.TrimLeftFunc(v, unicode.IsSpace) // if the first character is one of our separators // consume the separator and any whitespace if rest[0] == ';' || rest[0] == ',' { rest = strings.TrimLeftFunc(rest[1:], unicode.IsSpace) } // consume the parameter name and make sure it's not empty param, rest = consumeToken(rest) if param == "" { return "", "", v } // trim spaces and make sure we are at the '=' separator rest = strings.TrimLeftFunc(rest, unicode.IsSpace) if !strings.HasPrefix(rest, "=") { return "", "", v } // consume the equals sign and any whitespace rest = strings.TrimLeftFunc(rest[1:], unicode.IsSpace) // get the value of the parameter and make sure it's not empty value, rest2 := consumeValue(rest) if value == "" && rest2 == rest { return "", "", v } rest = rest2 return strings.ToLower(param), value, rest } // consumeValue consumes a "value" per RFC 2045, where a value is // either a 'token' or a 'quoted-string'. On success, consumeValue // returns the value consumed (and de-quoted/escaped, if a // quoted-string) and the rest of the string. On failure, returns // ("", v). func consumeValue(v string) (value, rest string) { if v == "" { return } if v[0] != '"' { return consumeToken(v) } // parse a quoted-string rest = v[1:] // consume the leading quote buffer := new(bytes.Buffer) var nextIsLiteral bool for idx, r := range rest { switch { case nextIsLiteral: buffer.WriteRune(r) nextIsLiteral = false case r == '"': return buffer.String(), rest[idx+1:] case r == '\\': nextIsLiteral = true case r != '\r' && r != '\n': buffer.WriteRune(r) default: return "", v } } return "", v } // consumeToken consumes a token from the beginning of provided // string, per RFC 2045 section 5.1 (referenced from 2183), and return // the token consumed and the rest of the string. Returns ("", v) on // failure to consume at least one character. func consumeToken(v string) (token, rest string) { notPos := strings.IndexFunc(v, isNotTokenChar) if notPos == -1 { return v, "" } if notPos == 0 { return "", v } return v[0:notPos], v[notPos:] } // isTSpecial reports whether rune is in 'tspecials' as defined by RFC // 1521 and RFC 2045. func isTSpecial(r rune) bool { return strings.ContainsRune(`()<>@,;:\"/[]?=`, r) } // isTokenChar reports whether rune is in 'token' as defined by RFC // 1521 and RFC 2045. func isTokenChar(r rune) bool { // token := 1* return r > 0x20 && r < 0x7f && !isTSpecial(r) } func isNotTokenChar(r rune) bool { return !isTokenChar(r) } golang-github-muhlemmer-httpforwarded-0.1.0/parse_test.go000066400000000000000000000143551456772654000236330ustar00rootroot00000000000000// Copyright 2016 Tim Heckman. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package httpforwarded_test import ( "github.com/muhlemmer/httpforwarded" . "gopkg.in/check.v1" "net/http" ) func (*TestSuite) TestParse(c *C) { testParseMisc(c) testParseSingleParam(c) testParseMultiParam(c) testParseMultiLine(c) testParseMultiParamValue(c) testParseAllTheThings(c) testParseFromRequest(c) } func testParseMisc(c *C) { var err error var vals map[string][]string vals, err = httpforwarded.Parse(nil) c.Check(err, IsNil) c.Check(vals, IsNil) var values []string vals, err = httpforwarded.Parse(values) c.Check(err, IsNil) c.Check(vals, IsNil) } func testParseSingleParam(c *C) { var err error var params map[string][]string values := []string{"for=192.0.2.1"} params, err = httpforwarded.Parse(values) c.Assert(err, IsNil) c.Check(len(params), Equals, 1) forVal, ok := params["for"] c.Assert(ok, Equals, true) c.Assert(len(forVal), Equals, 1) c.Check(forVal[0], Equals, "192.0.2.1") } func testParseMultiParam(c *C) { var err error var params map[string][]string values := []string{"for=192.0.2.1; proto=http"} params, err = httpforwarded.Parse(values) c.Assert(err, IsNil) c.Check(len(params), Equals, 2) forVal, ok := params["for"] c.Assert(ok, Equals, true) c.Assert(len(forVal), Equals, 1) c.Check(forVal[0], Equals, "192.0.2.1") proto, ok := params["proto"] c.Assert(ok, Equals, true) c.Assert(len(proto), Equals, 1) c.Check(proto[0], Equals, "http") } func testParseMultiLine(c *C) { var err error var params map[string][]string values := []string{ "for=192.0.2.1", "proto=http; by=192.0.2.200", } params, err = httpforwarded.Parse(values) c.Assert(err, IsNil) c.Check(len(params), Equals, 3) forVal, ok := params["for"] c.Assert(ok, Equals, true) c.Assert(len(forVal), Equals, 1) c.Check(forVal[0], Equals, "192.0.2.1") proto, ok := params["proto"] c.Assert(ok, Equals, true) c.Assert(len(proto), Equals, 1) c.Check(proto[0], Equals, "http") by, ok := params["by"] c.Assert(ok, Equals, true) c.Assert(len(by), Equals, 1) c.Check(by[0], Equals, "192.0.2.200") } func testParseMultiParamValue(c *C) { var err error var params map[string][]string values := []string{"for=192.0.2.1, for=192.0.2.4; proto=http"} params, err = httpforwarded.Parse(values) c.Assert(err, IsNil) c.Check(len(params), Equals, 2) forVal, ok := params["for"] c.Assert(ok, Equals, true) c.Assert(len(forVal), Equals, 2) c.Check(forVal[0], Equals, "192.0.2.1") c.Check(forVal[1], Equals, "192.0.2.4") proto, ok := params["proto"] c.Assert(ok, Equals, true) c.Assert(len(proto), Equals, 1) c.Check(proto[0], Equals, "http") } func testParseAllTheThings(c *C) { var err error var params map[string][]string values := []string{ "for=192.0.2.1; proto=http", "by=192.0.2.200", "for=192.0.2.4, for=192.0.2.10; by=192.0.2.202", } params, err = httpforwarded.Parse(values) c.Assert(err, IsNil) c.Check(len(params), Equals, 3) forVal, ok := params["for"] c.Assert(ok, Equals, true) c.Assert(len(forVal), Equals, 3) c.Check(forVal[0], Equals, "192.0.2.1") c.Check(forVal[1], Equals, "192.0.2.4") c.Check(forVal[2], Equals, "192.0.2.10") proto, ok := params["proto"] c.Assert(ok, Equals, true) c.Assert(len(proto), Equals, 1) c.Check(proto[0], Equals, "http") by, ok := params["by"] c.Assert(ok, Equals, true) c.Assert(len(by), Equals, 2) c.Check(by[0], Equals, "192.0.2.200") c.Check(by[1], Equals, "192.0.2.202") } func testParseFromRequest(c *C) { var err error var params map[string][]string req := &http.Request{Header: map[string][]string{ "Forwarded": {"for=192.0.2.1"}, }} params, err = httpforwarded.ParseFromRequest(req) c.Assert(err, IsNil) c.Check(len(params), Equals, 1) forVal, ok := params["for"] c.Assert(ok, Equals, true) c.Assert(len(forVal), Equals, 1) c.Check(forVal[0], Equals, "192.0.2.1") } func (*TestSuite) TestParseParameter(c *C) { testParseParameterMisc(c) testParseParameterSingleParam(c) testParseParameterMultiParam(c) testParseParameterMultiLine(c) testParseParameterAllTheThings(c) } func testParseParameterMisc(c *C) { var err error var vals []string vals, err = httpforwarded.ParseParameter("for", nil) c.Check(err, IsNil) c.Check(vals, IsNil) var values []string vals, err = httpforwarded.ParseParameter("for", values) c.Check(err, IsNil) c.Check(vals, IsNil) values = []string{"for=192.0.2.1"} vals, err = httpforwarded.ParseParameter("", values) c.Check(err, ErrorMatches, `paramName must not be ""`) c.Check(vals, IsNil) } func testParseParameterSingleParam(c *C) { var err error var vals []string values := []string{"for=192.0.2.1"} vals, err = httpforwarded.ParseParameter("for", values) c.Assert(err, IsNil) c.Check(len(vals), Equals, 1) c.Check(vals[0], Equals, "192.0.2.1") } func testParseParameterMultiParam(c *C) { var err error var vals []string values := []string{"for=192.0.2.1, for=192.0.2.4; proto=http"} vals, err = httpforwarded.ParseParameter("for", values) c.Assert(err, IsNil) c.Check(len(vals), Equals, 2) c.Check(vals[0], Equals, "192.0.2.1") c.Check(vals[1], Equals, "192.0.2.4") vals, err = httpforwarded.ParseParameter("proto", values) c.Assert(err, IsNil) c.Check(len(vals), Equals, 1) c.Check(vals[0], Equals, "http") } func testParseParameterMultiLine(c *C) { var err error var vals []string values := []string{"for=192.0.2.1", "for=192.0.2.4"} vals, err = httpforwarded.ParseParameter("for", values) c.Assert(err, IsNil) c.Check(len(vals), Equals, 2) c.Check(vals[0], Equals, "192.0.2.1") c.Check(vals[1], Equals, "192.0.2.4") } func testParseParameterAllTheThings(c *C) { var err error var vals []string values := []string{ "for=192.0.2.1; proto=http", "by=192.0.2.200", "for=192.0.2.4, for=192.0.2.10; by=192.0.2.202", } vals, err = httpforwarded.ParseParameter("for", values) c.Assert(err, IsNil) c.Check(len(vals), Equals, 3) c.Check(vals[0], Equals, "192.0.2.1") c.Check(vals[1], Equals, "192.0.2.4") c.Check(vals[2], Equals, "192.0.2.10") vals, err = httpforwarded.ParseParameter("by", values) c.Assert(err, IsNil) c.Check(len(vals), Equals, 2) c.Check(vals[0], Equals, "192.0.2.200") c.Check(vals[1], Equals, "192.0.2.202") }