From d0d8c2231fa1548401ddadd730675c91be048882 Mon Sep 17 00:00:00 2001 From: "Stamer, Daniel" Date: Wed, 11 Oct 2017 11:01:14 +0200 Subject: [PATCH 1/7] add documentation, add error handling Signed-off-by: Stamer, Daniel --- response.go | 24 +++++++++++++++++------- response_test.go | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/response.go b/response.go index e265e1f..8ba1d8f 100644 --- a/response.go +++ b/response.go @@ -17,6 +17,7 @@ import ( // BodyMode represent the current Body mode type BodyMode uint +// String to satisfy interface stringer func (m BodyMode) String() string { switch m { case BodyInput: @@ -32,12 +33,14 @@ const ( BodyFile ) +// Body is our response body content, that will either reference an local file or a runtime-supplied []byte. type Body struct { Mode BodyMode Input []byte File *os.File } +// Payload reads out a []byte payload according to it's configuration in Body.BodyMode. func (body *Body) Payload() []byte { switch body.Mode { case BodyInput: @@ -47,34 +50,40 @@ func (body *Body) Payload() []byte { return nil } - // XXX: Handle this error - bytes, _ := ioutil.ReadAll(body.File) + bytes, err := ioutil.ReadAll(body.File) + if err != nil { + return []byte(fmt.Sprintf("File could not be read: %s \n", body.File.Name())) + } body.File.Seek(0, 0) return bytes } - return nil + return []byte("No body configured.") } +// Info returns some basic info on the body. func (body *Body) Info() []byte { switch body.Mode { case BodyInput: - return body.Input + return []byte(fmt.Sprintf("size: %d bytes\n", len(body.Input))) case BodyFile: if body.File == nil { return nil } - // XXX: Handle this error - stats, _ := body.File.Stat() + stats, err := body.File.Stat() + if err != nil { + return []byte(fmt.Sprintf("File could not be read: %s \n", body.File.Name())) + } w := &bytes.Buffer{} fmt.Fprintf(w, "file: %s\n", body.File.Name()) fmt.Fprintf(w, "size: %d bytes\n", stats.Size()) fmt.Fprintf(w, "perm: %s\n", stats.Mode()) return w.Bytes() } - return nil + return []byte("No body configured.") } +// SetFile set a new source file for the body, if it exists. func (body *Body) SetFile(path string) error { file, err := os.Open(ExpandPath(path)) if err != nil { @@ -86,6 +95,7 @@ func (body *Body) SetFile(path string) error { return nil } +// Response is the the preconfigured HTTP response that will be returned to the client. type Response struct { Status int Headers http.Header diff --git a/response_test.go b/response_test.go index 986f9d5..8d87f01 100644 --- a/response_test.go +++ b/response_test.go @@ -146,13 +146,13 @@ func TestLoadFromJSON(t *testing.T) { r.Body.Mode = BodyInput assert.Equal(t, []byte("xxx"), r.Body.Payload()) - assert.Equal(t, []byte("xxx"), r.Body.Info()) + assert.Equal(t, []byte("size: 3 bytes\n"), r.Body.Info()) r.Body.Mode = BodyFile assert.Equal(t, []byte(""), r.Body.Payload()) t.Run("When config file is empty", func(t *testing.T) { - path := time.Now().Format(time.UnixDate) + path := string(time.Now().UnixNano()) defer os.Remove(path) require.NoError(t, rl.Load(path)) From fff2d399e529fc3cd987a0242dd64f8714f791aa Mon Sep 17 00:00:00 2001 From: "Stamer, Daniel" Date: Wed, 11 Oct 2017 11:36:56 +0200 Subject: [PATCH 2/7] completing documentation Signed-off-by: Stamer, Daniel --- response.go | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/response.go b/response.go index 8ba1d8f..c663d3a 100644 --- a/response.go +++ b/response.go @@ -103,6 +103,7 @@ type Response struct { Delay time.Duration } +// UnmarshalJSON inflats the Response from []byte representing JSON. func (r *Response) UnmarshalJSON(data []byte) error { type alias Response v := struct { @@ -140,6 +141,7 @@ func (r *Response) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON serializes the response into a JSON []byte. func (r *Response) MarshalJSON() ([]byte, error) { type alias Response v := struct { @@ -169,6 +171,7 @@ func (r *Response) MarshalJSON() ([]byte, error) { return json.MarshalIndent(v, "", " ") } +// NewResponse configures a new response. An empty status will be interpreted as 200 OK. func NewResponse(status, headers, body string) (*Response, error) { // Parse Status status = strings.Trim(status, " \r\n") @@ -177,7 +180,7 @@ func NewResponse(status, headers, body string) (*Response, error) { } code, err := strconv.Atoi(status) if err != nil { - return nil, fmt.Errorf("Status: %v", err) + return nil, fmt.Errorf("Could not interpret status: %v", err) } if code < 100 || code > 599 { @@ -210,6 +213,7 @@ func NewResponse(status, headers, body string) (*Response, error) { }, nil } +// Write satisfies interface io.Writer. func (r *Response) Write(w http.ResponseWriter) error { for key := range r.Headers { w.Header().Set(key, r.Headers.Get(key)) @@ -220,12 +224,14 @@ func (r *Response) Write(w http.ResponseWriter) error { return err } +// ResponsesList holds the multiple configured responses. type ResponsesList struct { List map[string]*Response keys []string current int } +// NewResponsesList clears the response list. func NewResponsesList() *ResponsesList { return (&ResponsesList{}).reset() } @@ -257,6 +263,7 @@ func (rl *ResponsesList) load(path string) (map[string]*Response, error) { return rs.Responses, nil } +// Load loads a response list from a local JSON document. func (rl *ResponsesList) Load(path string) error { rs, err := rl.load(path) if err != nil { @@ -268,7 +275,7 @@ func (rl *ResponsesList) Load(path string) error { rl.List = rs } - for key, _ := range rs { + for key := range rs { rl.keys = append(rl.keys, key) } sort.Strings(rl.keys) @@ -276,6 +283,7 @@ func (rl *ResponsesList) Load(path string) error { return nil } +// Save saves the current response list to a JSON document on local disk. func (rl *ResponsesList) Save(path string) error { f, err := openConfigFile(path) if err != nil { @@ -306,13 +314,28 @@ func (rl *ResponsesList) Save(path string) error { return nil } -func (rl *ResponsesList) Next() { rl.current = (rl.current + 1) % len(rl.keys) } -func (rl *ResponsesList) Prev() { rl.current = (rl.current - 1 + len(rl.keys)) % len(rl.keys) } -func (rl *ResponsesList) Cur() *Response { return rl.List[rl.keys[rl.current]] } -func (rl *ResponsesList) Index() int { return rl.current } -func (rl *ResponsesList) Len() int { return len(rl.keys) } -func (rl *ResponsesList) Keys() []string { return rl.keys } +// Next iterates to the next item in the response list. +func (rl *ResponsesList) Next() { rl.current = (rl.current + 1) % len(rl.keys) } + +// Prev iterates to the previous item in the response list. +func (rl *ResponsesList) Prev() { rl.current = (rl.current - 1 + len(rl.keys)) % len(rl.keys) } + +// Cur retrieves the current response from the response list. +func (rl *ResponsesList) Cur() *Response { return rl.List[rl.keys[rl.current]] } + +// Index retrieves the index of the current item in the response list. +func (rl *ResponsesList) Index() int { return rl.current } + +// Len reports the length of the response list. +func (rl *ResponsesList) Len() int { return len(rl.keys) } + +// Keys retrieves an []string of all keys in the response list. +func (rl *ResponsesList) Keys() []string { return rl.keys } + +// Get retrieves a specific response by name from the response list. func (rl *ResponsesList) Get(key string) *Response { return rl.List[key] } + +// Add appends a response item to the list. You need to supply a key for the item. func (rl *ResponsesList) Add(key string, r *Response) *ResponsesList { rl.keys = append(rl.keys, key) sort.Strings(rl.keys) @@ -320,6 +343,7 @@ func (rl *ResponsesList) Add(key string, r *Response) *ResponsesList { return rl } +// Del removes an item spceified by its key from the response list. It returns falls if the item didn't exist at all. func (rl *ResponsesList) Del(key string) bool { if _, ok := rl.List[key]; !ok { return false @@ -332,6 +356,7 @@ func (rl *ResponsesList) Del(key string) bool { return true } +// ExpandPath expands a given path by replacing '~' with $HOME of the current user. func ExpandPath(path string) string { if path[0] == '~' { path = "$HOME" + path[1:len(path)] From faf63f9429d782fc85ef3d77e201901e2b0085c0 Mon Sep 17 00:00:00 2001 From: Daniel Stamer Date: Fri, 13 Oct 2017 08:36:04 +0000 Subject: [PATCH 3/7] fixing reviews on PR #62 Signed-off-by: Daniel Stamer --- response.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/response.go b/response.go index c663d3a..46e632c 100644 --- a/response.go +++ b/response.go @@ -17,7 +17,7 @@ import ( // BodyMode represent the current Body mode type BodyMode uint -// String to satisfy interface stringer +// String to satisfy interface fmt.Stringer func (m BodyMode) String() string { switch m { case BodyInput: @@ -103,7 +103,7 @@ type Response struct { Delay time.Duration } -// UnmarshalJSON inflats the Response from []byte representing JSON. +// UnmarshalJSON inflates the Response from []byte representing JSON. func (r *Response) UnmarshalJSON(data []byte) error { type alias Response v := struct { @@ -213,7 +213,7 @@ func NewResponse(status, headers, body string) (*Response, error) { }, nil } -// Write satisfies interface io.Writer. +// Write flushes the body into the ResponseWriter, hence sending it over the wire. func (r *Response) Write(w http.ResponseWriter) error { for key := range r.Headers { w.Header().Set(key, r.Headers.Get(key)) @@ -231,7 +231,7 @@ type ResponsesList struct { current int } -// NewResponsesList clears the response list. +// NewResponsesList creates a new empty response list and returns it. func NewResponsesList() *ResponsesList { return (&ResponsesList{}).reset() } @@ -343,7 +343,7 @@ func (rl *ResponsesList) Add(key string, r *Response) *ResponsesList { return rl } -// Del removes an item spceified by its key from the response list. It returns falls if the item didn't exist at all. +// Del removes an item spceified by its key from the response list. It returns false if the item didn't exist at all. func (rl *ResponsesList) Del(key string) bool { if _, ok := rl.List[key]; !ok { return false From 1a2bd4ad05ee970c1f40316458ab8b524a96ab73 Mon Sep 17 00:00:00 2001 From: Daniel Stamer Date: Sat, 14 Oct 2017 08:01:58 +0000 Subject: [PATCH 4/7] revert to old Info() behavior on memory bodies Signed-off-by: Daniel Stamer --- response.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/response.go b/response.go index 46e632c..5b3f7f3 100644 --- a/response.go +++ b/response.go @@ -64,7 +64,7 @@ func (body *Body) Payload() []byte { func (body *Body) Info() []byte { switch body.Mode { case BodyInput: - return []byte(fmt.Sprintf("size: %d bytes\n", len(body.Input))) + return body.Input case BodyFile: if body.File == nil { return nil From 6fad45237f0578e7d3b2cc86501e85c0aeaec60b Mon Sep 17 00:00:00 2001 From: Daniel Stamer Date: Sat, 14 Oct 2017 08:05:32 +0000 Subject: [PATCH 5/7] fixing test cases to old Info() behavior Signed-off-by: Daniel Stamer --- response_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/response_test.go b/response_test.go index 8d87f01..49cdcc4 100644 --- a/response_test.go +++ b/response_test.go @@ -146,7 +146,7 @@ func TestLoadFromJSON(t *testing.T) { r.Body.Mode = BodyInput assert.Equal(t, []byte("xxx"), r.Body.Payload()) - assert.Equal(t, []byte("size: 3 bytes\n"), r.Body.Info()) + assert.Equal(t, []byte("xxx"), r.Body.Info()) r.Body.Mode = BodyFile assert.Equal(t, []byte(""), r.Body.Payload()) From d830673748ffe84ed62b0c32e256147431cbec2a Mon Sep 17 00:00:00 2001 From: "Stamer, Daniel" Date: Thu, 19 Oct 2017 09:51:04 +0200 Subject: [PATCH 6/7] removing error handling from PR #62 Signed-off-by: Stamer, Daniel --- response.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/response.go b/response.go index 5b3f7f3..e6b89fc 100644 --- a/response.go +++ b/response.go @@ -50,14 +50,12 @@ func (body *Body) Payload() []byte { return nil } - bytes, err := ioutil.ReadAll(body.File) - if err != nil { - return []byte(fmt.Sprintf("File could not be read: %s \n", body.File.Name())) - } + // XXX: Handle this error + bytes, _ := ioutil.ReadAll(body.File) body.File.Seek(0, 0) return bytes } - return []byte("No body configured.") + return nil } // Info returns some basic info on the body. @@ -70,17 +68,15 @@ func (body *Body) Info() []byte { return nil } - stats, err := body.File.Stat() - if err != nil { - return []byte(fmt.Sprintf("File could not be read: %s \n", body.File.Name())) - } + // XXX: Handle this error + stats, _ := body.File.Stat() w := &bytes.Buffer{} fmt.Fprintf(w, "file: %s\n", body.File.Name()) fmt.Fprintf(w, "size: %d bytes\n", stats.Size()) fmt.Fprintf(w, "perm: %s\n", stats.Mode()) return w.Bytes() } - return []byte("No body configured.") + return nil } // SetFile set a new source file for the body, if it exists. @@ -180,7 +176,7 @@ func NewResponse(status, headers, body string) (*Response, error) { } code, err := strconv.Atoi(status) if err != nil { - return nil, fmt.Errorf("Could not interpret status: %v", err) + return nil, fmt.Errorf("Status: %v", err) } if code < 100 || code > 599 { From 0d301e115789bc3f542e9ec414dab6cdc5d4a0dc Mon Sep 17 00:00:00 2001 From: "Stamer, Daniel" Date: Thu, 19 Oct 2017 10:00:13 +0200 Subject: [PATCH 7/7] adding (more verbose) error handling Signed-off-by: Stamer, Daniel --- response.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/response.go b/response.go index e6b89fc..5b3f7f3 100644 --- a/response.go +++ b/response.go @@ -50,12 +50,14 @@ func (body *Body) Payload() []byte { return nil } - // XXX: Handle this error - bytes, _ := ioutil.ReadAll(body.File) + bytes, err := ioutil.ReadAll(body.File) + if err != nil { + return []byte(fmt.Sprintf("File could not be read: %s \n", body.File.Name())) + } body.File.Seek(0, 0) return bytes } - return nil + return []byte("No body configured.") } // Info returns some basic info on the body. @@ -68,15 +70,17 @@ func (body *Body) Info() []byte { return nil } - // XXX: Handle this error - stats, _ := body.File.Stat() + stats, err := body.File.Stat() + if err != nil { + return []byte(fmt.Sprintf("File could not be read: %s \n", body.File.Name())) + } w := &bytes.Buffer{} fmt.Fprintf(w, "file: %s\n", body.File.Name()) fmt.Fprintf(w, "size: %d bytes\n", stats.Size()) fmt.Fprintf(w, "perm: %s\n", stats.Mode()) return w.Bytes() } - return nil + return []byte("No body configured.") } // SetFile set a new source file for the body, if it exists. @@ -176,7 +180,7 @@ func NewResponse(status, headers, body string) (*Response, error) { } code, err := strconv.Atoi(status) if err != nil { - return nil, fmt.Errorf("Status: %v", err) + return nil, fmt.Errorf("Could not interpret status: %v", err) } if code < 100 || code > 599 {