Repository: go-openapi/runtime
Pull request #127 (2019-02-12 22:33:31)

Add support for getting headers to ClientRequest
This is useful for things like implementing a `ClientAuthInfoWriterFunc` that [signs requests](https://tools.ietf.org/html/draft-cavage-http-signatures-09), for example.

This was just quickly thrown together in the GitHub UI. I'll do proper software engineering (e.g., tests) when I get a chance later.


Patch:
diff --git a/client/request.go b/client/request.go
index a1c1915..d53f66f 100644
--- a/client/request.go
+++ b/client/request.go
@@ -213,9 +213,6 @@ func (r *request) buildHTTP(mediaType, basePath string, producers map[string]run
 				return nil, err
 			}
 
-			if _, err := r.buf.Write(b.Bytes()); err != nil {
-				return nil, err
-			}
 			return ioutil.NopCloser(&b), nil
 		}
 
@@ -286,6 +283,11 @@ func (r *request) SetHeaderParam(name string, values ...string) error {
 	return nil
 }
 
+// GetHeaderParams returns the all headers currently set for the request
+func (r *request) GetHeaderParams() http.Header {
+	return r.header
+}
+
 // SetQueryParam adds a query param to the request
 // when there is only 1 value provided for the varargs, it will set it.
 // when there are several values provided for the varargs it will add it (no overriding)
diff --git a/client/runtime.go b/client/runtime.go
index c222d94..50c4fd4 100644
--- a/client/runtime.go
+++ b/client/runtime.go
@@ -207,6 +207,7 @@ func New(host, basePath string, schemes []string) *Runtime {
 		runtime.XMLMime:     runtime.XMLConsumer(),
 		runtime.TextMime:    runtime.TextConsumer(),
 		runtime.HTMLMime:    runtime.TextConsumer(),
+		runtime.CSVMime:     runtime.CSVConsumer(),
 		runtime.DefaultMime: runtime.ByteStreamConsumer(),
 	}
 	rt.Producers = map[string]runtime.Producer{
@@ -214,6 +215,7 @@ func New(host, basePath string, schemes []string) *Runtime {
 		runtime.XMLMime:     runtime.XMLProducer(),
 		runtime.TextMime:    runtime.TextProducer(),
 		runtime.HTMLMime:    runtime.TextProducer(),
+		runtime.CSVMime:     runtime.CSVProducer(),
 		runtime.DefaultMime: runtime.ByteStreamProducer(),
 	}
 	rt.Transport = http.DefaultTransport
diff --git a/client_request.go b/client_request.go
index 4aef7ed..904196a 100644
--- a/client_request.go
+++ b/client_request.go
@@ -17,6 +17,7 @@ package runtime
 import (
 	"io"
 	"io/ioutil"
+	"net/http"
 	"net/url"
 	"time"
 
@@ -40,6 +41,8 @@ type ClientRequestWriter interface {
 // add information to a swagger client request
 type ClientRequest interface {
 	SetHeaderParam(string, ...string) error
+	
+	GetHeaderParams() http.Header
 
 	SetQueryParam(string, ...string) error
 
diff --git a/constants.go b/constants.go
index 30f36fa..a4de897 100644
--- a/constants.go
+++ b/constants.go
@@ -38,6 +38,8 @@ const (
 	TextMime = "text/plain"
 	// HTMLMime the html mime type
 	HTMLMime = "text/html"
+	// CSVMime the csv mime type
+	CSVMime = "text/csv"
 	// MultipartFormMime the multipart form mime type
 	MultipartFormMime = "multipart/form-data"
 	// URLencodedFormMime the url encoded form mime type
diff --git a/csv.go b/csv.go
new file mode 100644
index 0000000..d807bd9
--- /dev/null
+++ b/csv.go
@@ -0,0 +1,77 @@
+// Copyright 2015 go-swagger maintainers
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runtime
+
+import (
+	"bytes"
+	"encoding/csv"
+	"errors"
+	"io"
+)
+
+// CSVConsumer creates a new CSV consumer
+func CSVConsumer() Consumer {
+	return ConsumerFunc(func(reader io.Reader, data interface{}) error {
+		if reader == nil {
+			return errors.New("CSVConsumer requires a reader")
+		}
+
+		csvReader := csv.NewReader(reader)
+		writer, ok := data.(io.Writer)
+		if !ok {
+			return errors.New("data type must be io.Writer")
+		}
+		csvWriter := csv.NewWriter(writer)
+		records, err := csvReader.ReadAll()
+		if err != nil {
+			return err
+		}
+		for _, r := range records {
+			if err := csvWriter.Write(r); err != nil {
+				return err
+			}
+		}
+		csvWriter.Flush()
+		return nil
+	})
+}
+
+// CSVProducer creates a new CSV producer
+func CSVProducer() Producer {
+	return ProducerFunc(func(writer io.Writer, data interface{}) error {
+		if writer == nil {
+			return errors.New("CSVProducer requires a writer")
+		}
+
+		dataBytes, ok := data.([]byte)
+		if !ok {
+			return errors.New("data type must be byte array")
+		}
+
+		csvReader := csv.NewReader(bytes.NewBuffer(dataBytes))
+		records, err := csvReader.ReadAll()
+		if err != nil {
+			return err
+		}
+		csvWriter := csv.NewWriter(writer)
+		for _, r := range records {
+			if err := csvWriter.Write(r); err != nil {
+				return err
+			}
+		}
+		csvWriter.Flush()
+		return nil
+	})
+}
diff --git a/middleware/context.go b/middleware/context.go
index 48d7150..54a8c21 100644
--- a/middleware/context.go
+++ b/middleware/context.go
@@ -16,10 +16,13 @@ package middleware
 
 import (
 	stdContext "context"
+	"fmt"
 	"net/http"
 	"strings"
 	"sync"
 
+	"github.com/go-openapi/runtime/security"
+
 	"github.com/go-openapi/analysis"
 	"github.com/go-openapi/errors"
 	"github.com/go-openapi/loads"
@@ -500,6 +503,11 @@ func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []st
 		if format == "" {
 			rw.Header().Set(runtime.HeaderContentType, runtime.JSONMime)
 		}
+
+		if realm := security.FailedBasicAuth(r); realm != "" {
+			rw.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", realm))
+		}
+
 		if route == nil || route.Operation == nil {
 			c.api.ServeErrorFor("")(rw, r, err)
 			return
diff --git a/middleware/router.go b/middleware/router.go
index 73c8eeb..539d847 100644
--- a/middleware/router.go
+++ b/middleware/router.go
@@ -188,7 +188,6 @@ func (ra *RouteAuthenticator) Authenticate(req *http.Request, route *MatchedRout
 			if !applies {
 				return false, nil, nil
 			}
-
 			if err != nil {
 				route.Authenticator = ra
 				return true, nil, err
diff --git a/middleware/spec.go b/middleware/spec.go
index 6e02b83..f029142 100644
--- a/middleware/spec.go
+++ b/middleware/spec.go
@@ -34,7 +34,7 @@ func Spec(basePath string, b []byte, next http.Handler) http.Handler {
 			rw.Header().Set("Content-Type", "application/json")
 			rw.WriteHeader(http.StatusOK)
 			//#nosec
-			rw.Write(b)
+			_, _ = rw.Write(b)
 			return
 		}
 
diff --git a/middleware/untyped/api.go b/middleware/untyped/api.go
index 3b0cd4e..e0bc1a7 100644
--- a/middleware/untyped/api.go
+++ b/middleware/untyped/api.go
@@ -245,8 +245,8 @@ func (d *API) validate() error {
 
 func (d *API) verify(name string, registrations []string, expectations []string) error {
 
-	sort.Sort(sort.StringSlice(registrations))
-	sort.Sort(sort.StringSlice(expectations))
+	sort.Strings(registrations)
+	sort.Strings(expectations)
 
 	expected := map[string]struct{}{}
 	seen := map[string]struct{}{}
@@ -271,8 +271,8 @@ func (d *API) verify(name string, registrations []string, expectations []string)
 	for k := range expected {
 		unregistered = append(unregistered, k)
 	}
-	sort.Sort(sort.StringSlice(unspecified))
-	sort.Sort(sort.StringSlice(unregistered))
+	sort.Strings(unspecified)
+	sort.Strings(unregistered)
 
 	if len(unregistered) > 0 || len(unspecified) > 0 {
 		return &errors.APIVerificationFailed{
diff --git a/security/authenticator.go b/security/authenticator.go
index 3c624d7..717d69c 100644
--- a/security/authenticator.go
+++ b/security/authenticator.go
@@ -69,27 +69,71 @@ type ScopedTokenAuthentication func(string, []string) (interface{}, error)
 // ScopedTokenAuthenticationCtx authentication function with context.Context
 type ScopedTokenAuthenticationCtx func(context.Context, string, []string) (context.Context, interface{}, error)
 
+var DefaultRealmName = "API"
+
+type secCtxKey uint8
+
+const (
+	failedBasicAuth secCtxKey = iota
+)
+
+func FailedBasicAuth(r *http.Request) string {
+	return FailedBasicAuthCtx(r.Context())
+}
+
+func FailedBasicAuthCtx(ctx context.Context) string {
+	v, ok := ctx.Value(failedBasicAuth).(string)
+	if !ok {
+		return ""
+	}
+	return v
+}
+
 // BasicAuth creates a basic auth authenticator with the provided authentication function
 func BasicAuth(authenticate UserPassAuthentication) runtime.Authenticator {
+	return BasicAuthRealm(DefaultRealmName, authenticate)
+}
+
+// BasicAuthBasicAuthRealm creates a basic auth authenticator with the provided authentication function and realm name
+func BasicAuthRealm(realm string, authenticate UserPassAuthentication) runtime.Authenticator {
+	if realm == "" {
+		realm = DefaultRealmName
+	}
+
 	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) {
 		if usr, pass, ok := r.BasicAuth(); ok {
 			p, err := authenticate(usr, pass)
+			if err != nil {
+				*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm))
+			}
 			return true, p, err
 		}
-
+		*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm))
 		return false, nil, nil
 	})
 }
 
 // BasicAuthCtx creates a basic auth authenticator with the provided authentication function with support for context.Context
 func BasicAuthCtx(authenticate UserPassAuthenticationCtx) runtime.Authenticator {
+	return BasicAuthRealmCtx(DefaultRealmName, authenticate)
+}
+
+// BasicAuthCtx creates a basic auth authenticator with the provided authentication function and realm name with support for context.Context
+func BasicAuthRealmCtx(realm string, authenticate UserPassAuthenticationCtx) runtime.Authenticator {
+	if realm == "" {
+		realm = DefaultRealmName
+	}
+
 	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) {
 		if usr, pass, ok := r.BasicAuth(); ok {
 			ctx, p, err := authenticate(r.Context(), usr, pass)
+			if err != nil {
+				ctx = context.WithValue(ctx, failedBasicAuth, realm)
+			}
 			*r = *r.WithContext(ctx)
 			return true, p, err
 		}
-
+		*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm))
 		return false, nil, nil
 	})
 }


Test patch:
diff --git a/client/request_test.go b/client/request_test.go
index e6ee6ac..c1db584 100644
--- a/client/request_test.go
+++ b/client/request_test.go
@@ -18,9 +18,14 @@ import (
 	"bytes"
 	"encoding/json"
 	"encoding/xml"
+	"errors"
+	"io"
 	"io/ioutil"
 	"mime"
 	"mime/multipart"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
 	"os"
 	"path/filepath"
 	"strings"
@@ -29,6 +34,7 @@ import (
 	"github.com/go-openapi/runtime"
 	"github.com/go-openapi/strfmt"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 var testProducers = map[string]runtime.Producer{
@@ -509,3 +515,81 @@ func TestBuildRequest_BuildHTTP_EscapedPath(t *testing.T) {
 		assert.Equal(t, runtime.JSONMime, req.Header.Get(runtime.HeaderContentType))
 	}
 }
+
+type testReqFn func(*testing.T, *http.Request)
+
+type testRoundTripper struct {
+	tr          http.RoundTripper
+	testFn      testReqFn
+	testHarness *testing.T
+}
+
+func (t *testRoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) {
+	t.testFn(t.testHarness, req)
+	return t.tr.RoundTrip(req)
+}
+
+func TestGetBodyCallsBeforeRoundTrip(t *testing.T) {
+
+	server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		rw.WriteHeader(http.StatusCreated)
+		_, err := rw.Write([]byte("test result"))
+		require.NoError(t, err)
+	}))
+	defer server.Close()
+	hu, _ := url.Parse(server.URL)
+
+	client := http.DefaultClient
+	transport := http.DefaultTransport
+
+	client.Transport = &testRoundTripper{
+		tr:          transport,
+		testHarness: t,
+		testFn: func(t *testing.T, req *http.Request) {
+			// Read the body once before sending the request
+			body, err := req.GetBody()
+			require.NoError(t, err)
+			bodyContent, err := ioutil.ReadAll(io.Reader(body))
+			require.EqualValues(t, req.ContentLength, len(bodyContent))
+			require.NoError(t, err)
+			require.EqualValues(t, "\"test body\"\n", string(bodyContent))
+
+			// Read the body a second time before sending the request
+			body, err = req.GetBody()
+			require.NoError(t, err)
+			bodyContent, err = ioutil.ReadAll(io.Reader(body))
+			require.NoError(t, err)
+			require.EqualValues(t, req.ContentLength, len(bodyContent))
+			require.EqualValues(t, "\"test body\"\n", string(bodyContent))
+		},
+	}
+
+	rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error {
+		return req.SetBodyParam("test body")
+	})
+
+	operation := &runtime.ClientOperation{
+		ID:          "getSites",
+		Method:      "POST",
+		PathPattern: "/",
+		Params:      rwrtr,
+		Client:      client,
+		Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
+			if response.Code() == http.StatusCreated {
+				var result string
+				if err := consumer.Consume(response.Body(), &result); err != nil {
+					return nil, err
+				}
+				return result, nil
+			}
+			return nil, errors.New("Unexpected error code")
+		}),
+	}
+
+	openAPIClient := New(hu.Host, "/", []string{"http"})
+	res, err := openAPIClient.Submit(operation)
+
+	require.NoError(t, err)
+	actual := res.(string)
+	require.EqualValues(t, "test result", actual)
+}
diff --git a/client/runtime_test.go b/client/runtime_test.go
index d1dccae..dfd6d7c 100644
--- a/client/runtime_test.go
+++ b/client/runtime_test.go
@@ -39,7 +39,6 @@ import (
 
 // task This describes a task. Tasks require a content property to be set.
 type task struct {
-
 	// Completed
 	Completed bool `json:"completed" xml:"completed"`
 
@@ -366,6 +365,50 @@ func TestRuntime_TextCanary(t *testing.T) {
 	}
 }
 
+func TestRuntime_CSVCanary(t *testing.T) {
+	// test that it can make a simple csv request
+	// and get the response for it.
+	result := `task,content,result
+1,task1,ok
+2,task2,fail
+`
+	server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		rw.Header().Add(runtime.HeaderContentType, runtime.CSVMime)
+		rw.WriteHeader(http.StatusOK)
+		_, _ = rw.Write([]byte(result))
+	}))
+	defer server.Close()
+
+	rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error {
+		return nil
+	})
+
+	hu, _ := url.Parse(server.URL)
+	rt := New(hu.Host, "/", []string{"http"})
+	res, err := rt.Submit(&runtime.ClientOperation{
+		ID:          "getTasks",
+		Method:      "GET",
+		PathPattern: "/",
+		Params:      rwrtr,
+		Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
+			if response.Code() == 200 {
+				var result bytes.Buffer
+				if err := consumer.Consume(response.Body(), &result); err != nil {
+					return nil, err
+				}
+				return result, nil
+			}
+			return nil, errors.New("Generic error")
+		}),
+	})
+
+	if assert.NoError(t, err) {
+		assert.IsType(t, bytes.Buffer{}, res)
+		actual := res.(bytes.Buffer)
+		assert.EqualValues(t, result, actual.String())
+	}
+}
+
 type roundTripperFunc func(*http.Request) (*http.Response, error)
 
 func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
diff --git a/csv_test.go b/csv_test.go
new file mode 100644
index 0000000..cfc4a93
--- /dev/null
+++ b/csv_test.go
@@ -0,0 +1,73 @@
+// Copyright 2015 go-swagger maintainers
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package runtime
+
+import (
+	"bytes"
+	"github.com/stretchr/testify/assert"
+	"io"
+	"net/http/httptest"
+	"testing"
+)
+
+const consProdCSV = `name,country,age
+John,US,19
+Mike,US,20
+`
+
+type csvEmptyReader struct{}
+
+func (r *csvEmptyReader) Read(d []byte) (int, error) {
+	return 0, io.EOF
+}
+
+func TestCSVConsumer(t *testing.T) {
+	cons := CSVConsumer()
+	reader := bytes.NewBuffer([]byte(consProdCSV))
+
+	outBuf := new(bytes.Buffer)
+	err := cons.Consume(reader, outBuf)
+	assert.NoError(t, err)
+	assert.Equal(t, consProdCSV, outBuf.String())
+
+	outBuf2 := new(bytes.Buffer)
+	err = cons.Consume(nil, outBuf2)
+	assert.Error(t, err)
+
+	err = cons.Consume(reader, struct{}{})
+	assert.Error(t, err)
+
+	emptyOutBuf := new(bytes.Buffer)
+	err = cons.Consume(&csvEmptyReader{}, emptyOutBuf)
+	assert.NoError(t, err)
+	assert.Equal(t, "", emptyOutBuf.String())
+}
+
+func TestCSVProducer(t *testing.T) {
+	prod := CSVProducer()
+	data := []byte(consProdCSV)
+
+	rw := httptest.NewRecorder()
+	err := prod.Produce(rw, data)
+	assert.NoError(t, err)
+	assert.Equal(t, consProdCSV, rw.Body.String())
+
+	rw2 := httptest.NewRecorder()
+	err = prod.Produce(rw2, struct{}{})
+	assert.Error(t, err)
+
+	err = prod.Produce(nil, data)
+	assert.Error(t, err)
+}
diff --git a/middleware/router_test.go b/middleware/router_test.go
index 931429c..3368780 100644
--- a/middleware/router_test.go
+++ b/middleware/router_test.go
@@ -50,7 +50,7 @@ func TestRouterMiddleware(t *testing.T) {
 	assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code)
 
 	methods := strings.Split(recorder.Header().Get("Allow"), ",")
-	sort.Sort(sort.StringSlice(methods))
+	sort.Strings(methods)
 	assert.Equal(t, "GET,POST", strings.Join(methods, ","))
 
 	recorder = httptest.NewRecorder()
@@ -82,7 +82,7 @@ func TestRouterMiddleware(t *testing.T) {
 	assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code)
 
 	methods = strings.Split(recorder.Header().Get("Allow"), ",")
-	sort.Sort(sort.StringSlice(methods))
+	sort.Strings(methods)
 	assert.Equal(t, "GET,POST", strings.Join(methods, ","))
 
 	recorder = httptest.NewRecorder()
@@ -232,11 +232,11 @@ func TestPathConverter(t *testing.T) {
 func TestExtractCompositParameters(t *testing.T) {
 	// name is the composite parameter's name, value is the value of this composit parameter, pattern is the pattern to be matched
 	cases := []struct {
-		name string
-		value string
+		name    string
+		value   string
 		pattern string
-		names []string
-		values []string
+		names   []string
+		values  []string
 	}{
 		{name: "fragment", value: "gie", pattern: "e", names: []string{"fragment"}, values: []string{"gi"}},
 		{name: "fragment", value: "t.simpson", pattern: ".{subfragment}", names: []string{"fragment", "subfragment"}, values: []string{"t", "simpson"}},
diff --git a/middleware/security_test.go b/middleware/security_test.go
index 7c283fa..8697ebc 100644
--- a/middleware/security_test.go
+++ b/middleware/security_test.go
@@ -41,6 +41,7 @@ func TestSecurityMiddleware(t *testing.T) {
 
 	mw.ServeHTTP(recorder, request)
 	assert.Equal(t, 401, recorder.Code)
+	assert.NotEmpty(t, recorder.Header().Get("WWW-Authenticate"))
 
 	recorder = httptest.NewRecorder()
 	request, _ = http.NewRequest("GET", "/api/pets", nil)
diff --git a/middleware/untyped/api_test.go b/middleware/untyped/api_test.go
index 551bec7..5d93adb 100644
--- a/middleware/untyped/api_test.go
+++ b/middleware/untyped/api_test.go
@@ -247,15 +247,15 @@ func TestUntypedAppValidation(t *testing.T) {
 	api2.RegisterProducer("application/x-yaml", new(stubProducer))
 
 	expected := []string{"application/x-yaml"}
-	sort.Sort(sort.StringSlice(expected))
+	sort.Strings(expected)
 	consumes := analyzed.ConsumesFor(analyzed.AllPaths()["/"].Get)
-	sort.Sort(sort.StringSlice(consumes))
+	sort.Strings(consumes)
 	assert.Equal(t, expected, consumes)
 	consumers := api1.ConsumersFor(consumes)
 	assert.Len(t, consumers, 1)
 
 	produces := analyzed.ProducesFor(analyzed.AllPaths()["/"].Get)
-	sort.Sort(sort.StringSlice(produces))
+	sort.Strings(produces)
 	assert.Equal(t, expected, produces)
 	producers := api1.ProducersFor(produces)
 	assert.Len(t, producers, 1)
diff --git a/security/basic_auth_test.go b/security/basic_auth_test.go
index 9b58e7a..213c9c1 100644
--- a/security/basic_auth_test.go
+++ b/security/basic_auth_test.go
@@ -52,6 +52,9 @@ func TestInvalidBasicAuth(t *testing.T) {
 	assert.Error(t, err)
 	assert.True(t, ok)
 	assert.Equal(t, "", usr)
+
+	assert.NotEmpty(t, FailedBasicAuth(req))
+	assert.Equal(t, DefaultRealmName, FailedBasicAuth(req))
 }
 
 func TestMissingbasicAuth(t *testing.T) {
@@ -63,6 +66,9 @@ func TestMissingbasicAuth(t *testing.T) {
 	assert.NoError(t, err)
 	assert.False(t, ok)
 	assert.Equal(t, nil, usr)
+
+	assert.NotEmpty(t, FailedBasicAuth(req))
+	assert.Equal(t, DefaultRealmName, FailedBasicAuth(req))
 }
 
 func TestNoRequestBasicAuth(t *testing.T) {