Background
I'm unmarshalling JSON data from an HTTP API to the following Golang structs:
type ResponseBody struct {
Version string `json:"jsonrpc"`
Result Result `json:"result"`
Error Error `json:"error"`
Id int `json:"id"`
}
type Result struct {
Random struct {
Data interface{} `json:"data"`
CompletionTime string `json:"completionTime"`
} `json:"random"`
BitsUsed int `json:"bitsUsed"`
BitsLeft int `json:"bitsLeft"`
RequestsLeft int `json:"requestsLeft"`
AdvisoryDelay int `json:"advisoryDelay"`
}
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
Data []int `json:"data,omitempty"`
}
and I've implemented the error interface for Error
as follows:
func (e Error) Error() string {
return fmt.Sprintf("Error: %+v", e)
}
Relevant code so far:
func Request(method string, params interface{}) (Result, error) {
// `error` in return types is _not_ a typo
body, err := json.Marshal(RequestBody{Version: "2.0", Params: params, Method: method, Id: 1})
if err != nil {
return Result{}, err
}
resp, err := http.Post(endpoint, "application/json-rpc", bytes.NewReader(body))
if err != nil {
return Result{}, fmt.Errorf("Request failed, error was %s", err)
}
defer resp.Body.Close()
text, err := ioutil.ReadAll(resp.Body)
if err != nil {
return Result{}, fmt.Errorf("Failed to read response into memory, error was: %s", err)
}
if resp.StatusCode != 200 {
var errorMessage Error
if err := json.Unmarshal(text, &errorMessage); err != nil {
return Result{}, Error{
Code: 409,
Message: fmt.Sprintf("Client could not decode JSON error response, received %s, error was: %s", text, err),
Data: []int{},
}
}
return Result{}, errorMessage
}
response := ResponseBody{}
if err := json.Unmarshal(text, &response); err != nil {
return Result{}, fmt.Errorf("Failed to JSON-decode response body, received %s from source, error was: %s", text, err)
}
return response.Result, response.Error
}
Issue
The following code hangs indefinitely on a successful call without panicking:
// `body` here is just any old struct
result, err := Request("generateIntegers", body)
if err != nil {
return []int{}, err // hangs here
}
In fact, the code always hangs when I invoke err
. No panic is raised and no error is returned - it is simply frozen[1].
[1] Strictly speaking, it causes a stack overflow error, but that's because the function never returns and so the deferred resp.Body.Close()
in Request
never gets called.
It gets weirder. Adding the following debugging lines:
response := ResponseBody{}
if err := json.Unmarshal(text, &response); err != nil {
return Result{}, fmt.Errorf("Failed to JSON-decode response body, received %s from source, error was: %s", text, err)
}
fmt.Println(response.Result.BitsUsed)
fmt.Println(response.Result) // this prints!
return response.Result, response.Error
works, but changing these lines to just
response := ResponseBody{}
if err := json.Unmarshal(text, &response); err != nil {
return Result{}, fmt.Errorf("Failed to JSON-decode response body, received %s from source, error was: %s", text, err)
}
fmt.Println(response.Result) // this no longer prints! :O
return response.Result, response.Error
causes Request
function itself to hang at this debug statement.
Things I've Tried
- Running a trace with
go test -trace
to view what's being called. This fails becausego tool trace
cannot parse the generated trace file. - Converting my signature to return
*error
instead of regularerror
. This did not help.
Why is this code snippet hanging?
Note: I am running Go 1.7