From 90c723d6b24616839c9b5eef780beade41911c49 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 29 Apr 2020 19:05:50 +0200 Subject: [PATCH] Staticcheck fixes (#196) * Staticcheck fixes This fixes all staticcheck warnings for this library. --- .travis.yml | 2 +- chan_test.go | 11 ++++++----- cli/cmd_suggestion.go | 14 ++++++++++---- cli/helptext.go | 17 +++++------------ cli/parse_test.go | 7 ++++--- cli/responseemitter_test.go | 8 +------- cli/single_test.go | 5 +++-- command.go | 9 +++++---- command_test.go | 11 ++++++----- executor_test.go | 4 ++-- http/config.go | 10 +++------- http/handler.go | 16 +++++----------- http/parse.go | 6 +++--- http/parse_test.go | 2 +- http/reforigin_test.go | 2 +- http/responseemitter.go | 17 +++++++---------- reqlog.go | 8 ++++++-- request.go | 12 ++++++------ response_test.go | 14 -------------- responseemitter_test.go | 27 ++++++++++++++++++--------- single_test.go | 6 ++++-- writer.go | 4 +++- 22 files changed, 100 insertions(+), 112 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5163d69..3d81468 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: language: go go: - - 1.11.x + - 1.14.2 env: global: diff --git a/chan_test.go b/chan_test.go index d65efc8..84c0246 100644 --- a/chan_test.go +++ b/chan_test.go @@ -29,12 +29,12 @@ func TestChanResponsePair(t *testing.T) { wg.Add(1) go func() { for _, v := range tc.values { - v_, err := res.Next() + v2, err := res.Next() if err != nil { t.Error("Next returned unexpected error:", err) } - if v != v_ { - t.Errorf("Next returned unexpected value %q, expected %q", v_, v) + if v != v2 { + t.Errorf("Next returned unexpected value %q, expected %q", v2, v) } } @@ -93,7 +93,7 @@ func TestSingle1(t *testing.T) { err := re.Close() if err != ErrClosingClosedEmitter { - t.Fatalf("expected double close error, got %v", err) + t.Errorf("expected double close error, got %v", err) } close(wait) }() @@ -127,7 +127,8 @@ func TestSingle2(t *testing.T) { go func() { err := re.Emit(Single{42}) if err != ErrClosedEmitter { - t.Fatal("expected closed emitter error, got", err) + t.Error("expected closed emitter error, got", err) + return } }() diff --git a/cli/cmd_suggestion.go b/cli/cmd_suggestion.go index b3a20f4..9b80402 100644 --- a/cli/cmd_suggestion.go +++ b/cli/cmd_suggestion.go @@ -38,7 +38,7 @@ func suggestUnknownCmd(args []string, root *cmds.Command) []string { var suggestions []string sortableSuggestions := make(suggestionSlice, 0) var sFinal []string - const MIN_LEVENSHTEIN = 3 + const MinLevenshtein = 3 var options levenshtein.Options = levenshtein.Options{ InsCost: 1, @@ -50,7 +50,7 @@ func suggestUnknownCmd(args []string, root *cmds.Command) []string { } // Start with a simple strings.Contains check - for name, _ := range root.Subcommands { + for name := range root.Subcommands { if strings.Contains(arg, name) { suggestions = append(suggestions, name) } @@ -61,9 +61,9 @@ func suggestUnknownCmd(args []string, root *cmds.Command) []string { return suggestions } - for name, _ := range root.Subcommands { + for name := range root.Subcommands { lev := levenshtein.DistanceForStrings([]rune(arg), []rune(name), options) - if lev <= MIN_LEVENSHTEIN { + if lev <= MinLevenshtein { sortableSuggestions = append(sortableSuggestions, &suggestion{name, lev}) } } @@ -78,11 +78,17 @@ func suggestUnknownCmd(args []string, root *cmds.Command) []string { func printSuggestions(inputs []string, root *cmds.Command) (err error) { suggestions := suggestUnknownCmd(inputs, root) + if len(suggestions) > 1 { + //lint:ignore ST1005 user facing error err = fmt.Errorf("Unknown Command \"%s\"\n\nDid you mean any of these?\n\n\t%s", inputs[0], strings.Join(suggestions, "\n\t")) + } else if len(suggestions) > 0 { + //lint:ignore ST1005 user facing error err = fmt.Errorf("Unknown Command \"%s\"\n\nDid you mean this?\n\n\t%s", inputs[0], suggestions[0]) + } else { + //lint:ignore ST1005 user facing error err = fmt.Errorf("Unknown Command \"%s\"\n", inputs[0]) } return diff --git a/cli/helptext.go b/cli/helptext.go index 7b54d7f..5458bb2 100644 --- a/cli/helptext.go +++ b/cli/helptext.go @@ -137,8 +137,11 @@ func init() { shortHelpTemplate = template.Must(template.New("shortHelp").Parse(shortHelpFormat)) } +// ErrNoHelpRequested returns when request for help help does not include the +// short nor the long option. var ErrNoHelpRequested = errors.New("no help requested") +// HandleHelp writes help to a writer for the given request's command. func HandleHelp(appName string, req *cmds.Request, out io.Writer) error { long, _ := req.Options[cmds.OptLongHelp].(bool) short, _ := req.Options[cmds.OptShortHelp].(bool) @@ -369,18 +372,15 @@ func appendWrapped(prefix, text string, width int) string { func optionFlag(flag string) string { if len(flag) == 1 { return fmt.Sprintf(shortFlag, flag) - } else { - return fmt.Sprintf(longFlag, flag) } + return fmt.Sprintf(longFlag, flag) } func optionText(width int, cmd ...*cmds.Command) []string { // get a slice of the options we want to list out options := make([]cmds.Option, 0) for _, c := range cmd { - for _, opt := range c.Options { - options = append(options, opt) - } + options = append(options, c.Options...) } // add option names to output @@ -503,13 +503,6 @@ func align(lines []string) []string { return lines } -func indent(lines []string, prefix string) []string { - for i, line := range lines { - lines[i] = prefix + indentString(line, prefix) - } - return lines -} - func indentString(line string, prefix string) string { return prefix + strings.Replace(line, "\n", "\n"+prefix, -1) } diff --git a/cli/parse_test.go b/cli/parse_test.go index 247e5a3..bc6e2ae 100644 --- a/cli/parse_test.go +++ b/cli/parse_test.go @@ -3,7 +3,6 @@ package cli import ( "context" "fmt" - "github.com/ipfs/go-ipfs-files" "io" "io/ioutil" "net/url" @@ -12,6 +11,8 @@ import ( "strings" "testing" + files "github.com/ipfs/go-ipfs-files" + cmds "github.com/ipfs/go-ipfs-cmds" ) @@ -211,7 +212,7 @@ func TestArgumentParsing(t *testing.T) { test := func(cmd words, f *os.File, res words) { if f != nil { - if _, err := f.Seek(0, os.SEEK_SET); err != nil { + if _, err := f.Seek(0, io.SeekStart); err != nil { t.Fatal(err) } } @@ -486,7 +487,7 @@ func TestBodyArgs(t *testing.T) { for _, tc := range tcs { if tc.f != nil { - if _, err := tc.f.Seek(0, os.SEEK_SET); err != nil { + if _, err := tc.f.Seek(0, io.SeekStart); err != nil { t.Fatal(err) } } diff --git a/cli/responseemitter_test.go b/cli/responseemitter_test.go index 4412f62..278c98f 100644 --- a/cli/responseemitter_test.go +++ b/cli/responseemitter_test.go @@ -5,15 +5,9 @@ import ( "fmt" "testing" - "github.com/ipfs/go-ipfs-cmds" + cmds "github.com/ipfs/go-ipfs-cmds" ) -type writeCloser struct { - *bytes.Buffer -} - -func (wc writeCloser) Close() error { return nil } - type tcCloseWithError struct { stdout, stderr *bytes.Buffer exStdout, exStderr string diff --git a/cli/single_test.go b/cli/single_test.go index aff6922..858d374 100644 --- a/cli/single_test.go +++ b/cli/single_test.go @@ -5,7 +5,7 @@ import ( "context" "testing" - "github.com/ipfs/go-ipfs-cmds" + cmds "github.com/ipfs/go-ipfs-cmds" ) func TestSingle(t *testing.T) { @@ -25,7 +25,8 @@ func TestSingle(t *testing.T) { go func() { if err := cmds.EmitOnce(re, "test"); err != nil { - t.Fatal(err) + t.Error(err) + return } err := re.Emit("this should not be emitted") diff --git a/command.go b/command.go index b4a1199..9acb8c9 100644 --- a/command.go +++ b/command.go @@ -13,11 +13,12 @@ import ( "fmt" "strings" - "github.com/ipfs/go-ipfs-files" + files "github.com/ipfs/go-ipfs-files" logging "github.com/ipfs/go-log" ) +// DefaultOutputEncoding defines the default API output encoding. const DefaultOutputEncoding = JSON var log = logging.Logger("cmds") @@ -93,13 +94,13 @@ type Command struct { var ( // ErrNotCallable signals a command that cannot be called. - ErrNotCallable = ClientError("This command can't be called directly. Try one of its subcommands.") + ErrNotCallable = ClientError("this command cannot be called directly; try one of its subcommands.") // ErrNoFormatter signals that the command can not be formatted. - ErrNoFormatter = ClientError("This command cannot be formatted to plain text") + ErrNoFormatter = ClientError("this command cannot be formatted to plain text") // ErrIncorrectType signales that the commands returned a value with unexpected type. - ErrIncorrectType = errors.New("The command returned a value with a different type than expected") + ErrIncorrectType = errors.New("the command returned a value with a different type than expected") ) // Call invokes the command for the given Request diff --git a/command_test.go b/command_test.go index ee9a99c..fdfc0c9 100644 --- a/command_test.go +++ b/command_test.go @@ -39,7 +39,7 @@ func TestOptionValidation(t *testing.T) { cmd.Call(req, re, nil) } else { if err == nil { - t.Errorf("Should have failed with error %q", tc.NewRequestError) + t.Errorf("should have failed with error %q", tc.NewRequestError) } else if err.Error() != tc.NewRequestError { t.Errorf("expected error %q, got %q", tc.NewRequestError, err) } @@ -50,7 +50,7 @@ func TestOptionValidation(t *testing.T) { tcs := []testcase{ { opts: map[string]interface{}{"boop": true}, - NewRequestError: `Option "boop" should be type "string", but got type "bool"`, + NewRequestError: `option "boop" should be type "string", but got type "bool"`, }, {opts: map[string]interface{}{"beep": 5}}, {opts: map[string]interface{}{"beep": 5, "boop": "test"}}, @@ -61,10 +61,10 @@ func TestOptionValidation(t *testing.T) { {opts: map[string]interface{}{"S": [2]string{"a", "b"}}}, { opts: map[string]interface{}{"S": true}, - NewRequestError: `Option "S" should be type "array", but got type "bool"`}, + NewRequestError: `option "S" should be type "array", but got type "bool"`}, { opts: map[string]interface{}{"beep": ":)"}, - NewRequestError: `Could not convert value ":)" to type "int" (for option "-beep")`, + NewRequestError: `could not convert value ":)" to type "int" (for option "-beep")`, }, } @@ -279,7 +279,8 @@ func TestPostRun(t *testing.T) { t.Log("next returned", v, err) if err != nil { close(ch) - t.Fatal(err) + t.Error(err) + return } ch <- v diff --git a/executor_test.go b/executor_test.go index 86c1bd6..71605ab 100644 --- a/executor_test.go +++ b/executor_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -var theError = errors.New("an error occurred") +var errGeneric = errors.New("an error occurred") var root = &Command{ Subcommands: map[string]*Command{ @@ -20,7 +20,7 @@ var root = &Command{ }, "testError": &Command{ Run: func(req *Request, re ResponseEmitter, env Environment) error { - err := theError + err := errGeneric if err != nil { return err } diff --git a/http/config.go b/http/config.go index 6dc3877..7c66187 100644 --- a/http/config.go +++ b/http/config.go @@ -170,15 +170,11 @@ func allowUserAgent(r *http.Request, cfg *ServerConfig) bool { return true } - // Allow if the user agent does not start with Mozilla... (i.e. curl) - ua := r.Header.Get("User-agent") - if !strings.HasPrefix(ua, "Mozilla") { - return true - } - + // Allow if the user agent does not start with Mozilla... (i.e. curl). // Disallow otherwise. // // This means the request probably came from a browser and thus, it // should have included Origin or referer headers. - return false + ua := r.Header.Get("User-agent") + return !strings.HasPrefix(ua, "Mozilla") } diff --git a/http/handler.go b/http/handler.go index 2152ea9..a1756f6 100644 --- a/http/handler.go +++ b/http/handler.go @@ -5,7 +5,6 @@ import ( "errors" "net/http" "runtime/debug" - "strings" "sync" "time" @@ -17,11 +16,12 @@ import ( var log = logging.Logger("cmds/http") var ( - ErrNotFound = errors.New("404 page not found") - errApiVersionMismatch = errors.New("api version mismatch") + // ErrNotFound is returned when the endpoint does not exist. + ErrNotFound = errors.New("404 page not found") ) const ( + // StreamErrHeader is used as trailer when stream errors happen. StreamErrHeader = "X-Stream-Error" streamHeader = "X-Stream-Output" channelHeader = "X-Chunked-Output" @@ -32,7 +32,7 @@ const ( transferEncodingHeader = "Transfer-Encoding" originHeader = "origin" - applicationJson = "application/json" + applicationJSON = "application/json" applicationOctetStream = "application/octet-stream" plainText = "text/plain" ) @@ -57,6 +57,7 @@ type handler struct { env cmds.Environment } +// NewHandler creates the http.Handler for the given commands. func NewHandler(env cmds.Environment, root *cmds.Command, cfg *ServerConfig) http.Handler { if cfg == nil { panic("must provide a valid ServerConfig") @@ -190,13 +191,6 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.root.Call(req, re, h.env) } -func sanitizedErrStr(err error) string { - s := err.Error() - s = strings.Split(s, "\n")[0] - s = strings.Split(s, "\r")[0] - return s -} - func setAllowedHeaders(w http.ResponseWriter, allowGet bool) { w.Header().Add("Allow", http.MethodHead) w.Header().Add("Allow", http.MethodOptions) diff --git a/http/parse.go b/http/parse.go index f8d6677..d35c281 100644 --- a/http/parse.go +++ b/http/parse.go @@ -10,9 +10,9 @@ import ( "strconv" "strings" - "github.com/ipfs/go-ipfs-cmds" + cmds "github.com/ipfs/go-ipfs-cmds" - "github.com/ipfs/go-ipfs-files" + files "github.com/ipfs/go-ipfs-files" logging "github.com/ipfs/go-log" ) @@ -133,7 +133,7 @@ func parseRequest(r *http.Request, root *cmds.Command) (*cmds.Request, error) { // if there is a required filearg, error if no files were provided if len(requiredFile) > 0 && f == nil { - return nil, fmt.Errorf("File argument '%s' is required", requiredFile) + return nil, fmt.Errorf("file argument '%s' is required", requiredFile) } ctx := logging.ContextWithLoggable(r.Context(), uuidLoggable()) diff --git a/http/parse_test.go b/http/parse_test.go index 7e5ceae..1d903c3 100644 --- a/http/parse_test.go +++ b/http/parse_test.go @@ -47,7 +47,7 @@ func TestParse(t *testing.T) { if err != nil { t.Fatal(err) } - req, err = parseRequest(r, root) + _, err = parseRequest(r, root) if err != ErrNotFound { t.Errorf("expected ErrNotFound, got: %v", err) } diff --git a/http/reforigin_test.go b/http/reforigin_test.go index e7dde10..915673c 100644 --- a/http/reforigin_test.go +++ b/http/reforigin_test.go @@ -384,7 +384,7 @@ func TestEncoding(t *testing.T) { } tcs := []httpTestCase{ - gtc(cmds.JSON, applicationJson), + gtc(cmds.JSON, applicationJSON), gtc(cmds.XML, "application/xml"), } diff --git a/http/responseemitter.go b/http/responseemitter.go index a405cec..e5d424e 100644 --- a/http/responseemitter.go +++ b/http/responseemitter.go @@ -8,14 +8,14 @@ import ( "strings" "sync" - "github.com/ipfs/go-ipfs-cmds" + cmds "github.com/ipfs/go-ipfs-cmds" ) var ( - HeadRequest = fmt.Errorf("HEAD request") - + // AllowedExposedHeadersArr defines the default Access-Control-Expose-Headers. AllowedExposedHeadersArr = []string{streamHeader, channelHeader, extraContentLengthHeader} - AllowedExposedHeaders = strings.Join(AllowedExposedHeadersArr, ", ") + // AllowedExposedHeaders is the list of defaults Access-Control-Expose-Headers separated by comma. + AllowedExposedHeaders = strings.Join(AllowedExposedHeadersArr, ", ") mimeTypes = map[cmds.EncodingType]string{ cmds.Protobuf: "application/protobuf", @@ -25,7 +25,7 @@ var ( } ) -// NewResponeEmitter returns a new ResponseEmitter. +// NewResponseEmitter returns a new ResponseEmitter. func NewResponseEmitter(w http.ResponseWriter, method string, req *cmds.Request, opts ...ResponseEmitterOption) (ResponseEmitter, error) { encType, enc, err := cmds.GetEncoder(req, w, cmds.JSON) if err != nil { @@ -61,6 +61,8 @@ func withRequestBodyEOFChan(ch <-chan struct{}) ResponseEmitterOption { } } +// ResponseEmitter interface defines the components that can care of sending +// the response to HTTP Requests. type ResponseEmitter interface { cmds.ResponseEmitter http.Flusher @@ -75,7 +77,6 @@ type responseEmitter struct { l sync.Mutex length uint64 - err *cmds.Error bodyEOFChan <-chan struct{} @@ -315,10 +316,6 @@ func (re *responseEmitter) doPreamble(value interface{}) { re.w.WriteHeader(http.StatusOK) } -type responseWriterer interface { - Lower() http.ResponseWriter -} - func flushCopy(w io.Writer, r io.Reader) error { buf := make([]byte, 4096) f, ok := w.(http.Flusher) diff --git a/reqlog.go b/reqlog.go index fcedb4b..fdc3c55 100644 --- a/reqlog.go +++ b/reqlog.go @@ -6,6 +6,7 @@ import ( "time" ) +// ReqLogEntry represent a log entry for a request. type ReqLogEntry struct { StartTime time.Time EndTime time.Time @@ -16,11 +17,13 @@ type ReqLogEntry struct { ID int } +// Copy copies a log entry and returns a pointer to the copy. func (r *ReqLogEntry) Copy() *ReqLogEntry { out := *r return &out } +// ReqLog represents a request log. type ReqLog struct { Requests []*ReqLogEntry nextID int @@ -28,6 +31,7 @@ type ReqLog struct { keep time.Duration } +// Add ads an entry to the log for the given request. func (rl *ReqLog) Add(req *Request) *ReqLogEntry { rle := &ReqLogEntry{ StartTime: time.Now(), @@ -42,6 +46,7 @@ func (rl *ReqLog) Add(req *Request) *ReqLogEntry { return rle } +// AddEntry adds an entry to the log. func (rl *ReqLog) AddEntry(rle *ReqLogEntry) { rl.lock.Lock() defer rl.lock.Unlock() @@ -52,10 +57,9 @@ func (rl *ReqLog) AddEntry(rle *ReqLogEntry) { if rle == nil || !rle.Active { rl.maybeCleanup() } - - return } +// ClearInactive clears any inactive requests from the log. func (rl *ReqLog) ClearInactive() { rl.lock.Lock() defer rl.lock.Unlock() diff --git a/request.go b/request.go index b12f945..4eca412 100644 --- a/request.go +++ b/request.go @@ -66,6 +66,7 @@ func (req *Request) BodyArgs() StdinArguments { return nil } +// ParseBodyArgs parses arguments in the request body. func (req *Request) ParseBodyArgs() error { s := req.BodyArgs() if s == nil { @@ -78,6 +79,7 @@ func (req *Request) ParseBodyArgs() error { return s.Err() } +// SetOption sets a request option. func (req *Request) SetOption(name string, value interface{}) { optDefs, err := req.Root.GetOptions(req.Path) optDef, found := optDefs[name] @@ -95,8 +97,6 @@ func (req *Request) SetOption(name string, value interface{}) { name = optDef.Name() req.Options[name] = value - - return } func checkAndConvertOptions(root *Command, opts OptMap, path []string) (OptMap, error) { @@ -125,20 +125,20 @@ func checkAndConvertOptions(root *Command, opts OptMap, path []string) (OptMap, if len(str) == 0 { value = "empty value" } - return options, fmt.Errorf("Could not convert %s to type %q (for option %q)", + return options, fmt.Errorf("could not convert %s to type %q (for option %q)", value, opt.Type().String(), "-"+k) } options[k] = val } else { - return options, fmt.Errorf("Option %q should be type %q, but got type %q", + return options, fmt.Errorf("option %q should be type %q, but got type %q", k, opt.Type().String(), kind.String()) } } for _, name := range opt.Names() { if _, ok := options[name]; name != k && ok { - return options, fmt.Errorf("Duplicate command options were provided (%q and %q)", + return options, fmt.Errorf("duplicate command options were provided (%q and %q)", k, name) } } @@ -162,7 +162,7 @@ func GetEncoding(req *Request, def EncodingType) EncodingType { } } -// fillDefault fills in default values if option has not been set +// FillDefaults fills in default values if option has not been set. func (req *Request) FillDefaults() error { optDefMap, err := req.Root.GetOptions(req.Path) if err != nil { diff --git a/response_test.go b/response_test.go index 1c690d8..cf0b85c 100644 --- a/response_test.go +++ b/response_test.go @@ -12,20 +12,6 @@ type TestOutput struct { Baz int } -func eqStringSlice(a, b []string) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} - func TestMarshalling(t *testing.T) { cmd := &Command{} diff --git a/responseemitter_test.go b/responseemitter_test.go index 11e9f0c..13e9bea 100644 --- a/responseemitter_test.go +++ b/responseemitter_test.go @@ -19,18 +19,21 @@ func TestCopy(t *testing.T) { go func() { err := Copy(re2, res1) if err != nil { - t.Fatal(err) + t.Error(err) + return } }() go func() { err := re1.Emit("test") if err != nil { - t.Fatal(err) + t.Error(err) + return } err = re1.Close() if err != nil { - t.Fatal(err) + t.Error(err) + return } }() @@ -64,18 +67,21 @@ func TestCopyError(t *testing.T) { go func() { err := Copy(re2, res1) if err != nil { - t.Fatal(err) + t.Error(err) + return } }() go func() { err := re1.Emit("test") if err != nil { - t.Fatal(err) + t.Error(err) + return } err = re1.CloseWithError(fooErr) if err != nil { - t.Fatal(err) + t.Error(err) + return } }() @@ -106,17 +112,20 @@ func TestError(t *testing.T) { go func() { err := re.Emit("value1") if err != nil { - t.Fatal(err) + t.Error(err) + return } err = re.Emit("value2") if err != nil { - t.Fatal(err) + t.Error(err) + return } err = re.CloseWithError(&Error{Message: "foo"}) if err != nil { - t.Fatal(err) + t.Error(err) + return } }() diff --git a/single_test.go b/single_test.go index 4d9dc32..a27a642 100644 --- a/single_test.go +++ b/single_test.go @@ -22,7 +22,8 @@ func TestSingleChan(t *testing.T) { defer wg.Done() if err := EmitOnce(re, "test"); err != nil { - t.Fatal(err) + t.Error(err) + return } err := re.Emit("test") @@ -73,7 +74,8 @@ func TestSingleWriter(t *testing.T) { wg.Add(1) go func() { if err := EmitOnce(re, "test"); err != nil { - t.Fatal(err) + t.Error(err) + return } err := re.Emit("this should not be sent") diff --git a/writer.go b/writer.go index 97737a4..ad31224 100644 --- a/writer.go +++ b/writer.go @@ -8,6 +8,8 @@ import ( "sync" ) +// NewWriterResponseEmitter creates a response emitter that sends responses to +// the given WriterCloser. func NewWriterResponseEmitter(w io.WriteCloser, req *Request) (ResponseEmitter, error) { _, valEnc, err := GetEncoder(req, w, Undefined) if err != nil { @@ -24,6 +26,7 @@ func NewWriterResponseEmitter(w io.WriteCloser, req *Request) (ResponseEmitter, return re, nil } +// NewReaderResponse creates a Response from the given reader. func NewReaderResponse(r io.Reader, req *Request) (Response, error) { encType := GetEncoding(req, Undefined) dec, ok := Decoders[encType] @@ -99,7 +102,6 @@ type writerResponseEmitter struct { req *Request length *uint64 - err *Error emitted bool closed bool -- GitLab