http override http header code in golang while there is an error in json encoding - http

consider this scenario!
after successful execution of a http request, what if there is an error while performing json encoding, how to override the header code
func writeResp(w http.ResponseWriter, code int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
//Here I set the status to 201 StatusCreated
w.WriteHeader(code)
s := success{Data: data}
//what if there is an error here and want to override the status to 5xx error
//how to handle error here, panic?, http.Error() is not an option because as we already wrote header to 201, it just prints `http: multiple response.WriteHeader calls`
if err := json.NewEncoder(w).Encode(s); err != nil {
w.Header().Set("Content-Type", "application/json")
//it throws http: multiple response.WriteHeader calls here as we already wrote header above to 201
w.WriteHeader(code)
e := errorResponse{
Code: code,
Error: error,
Description: msg,
}
if err := json.NewEncoder(w).Encode(e); err != nil {
//same how to handle here
}
}
}
I have multiple options here, if we do just fatal logging the user won't know exactly what happened, even if I write string using w.Write([]byte(msg)) still the status says 201 created, how to respond with error code 5xx
any help is greatly appreciated

First of all, it does not seem very likely that you get an error when encoding.
See this question for reasons for Marshal to fail:
What input will cause golang's json.Marshal to return an error?
The other potential cause of error would be some problem with actually writing the data to the response stream, but in that case you'd not be able to write your custom error either.
Going back to your question, if you are concerned that encoding your object might fail, you can first Marshal your data (checking for error), then only write the 201 status code (and the encoded data) if marshalling succeeded.
Modifying your example a bit:
s := success{Data: data}
jsonData, err := json.Marshal(s)
if err != nil {
// write your error to w, then return
}
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/json")
w.Write(jsonData)
Now, that last write can also throw an error.
But if that happens, it will also fail when writing your custom error, so in that case you'd better log that in the server side (or send that error to a tracker such as New Relic, etc).

Related

Go HTTP RoundTripper: Preventing Connection Reuse Based on Response

I have a use case where I want to use an HTTP client in Go with pooled connections (connection re-use), but with the special case where a connection is intentionally closed (not allowed for re-use) if a request on that connection returns a specific HTTP status code.
I've implemented a custom http.RoundTripper, which wraps an http.Transport, and can inspect the response status code. However, I can't seem to find a way to prevent the http.Transport from re-using that connection, without also preventing it from re-using any other connection.
Is this possible using the net/http package? If not, any suggested workaround for accomplishing this?
My current code looks something like this:
type MyTransport struct {
transport *http.Transport
}
func (mt *MyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := tt.transport.RoundTrip(req)
if err != nil {
return resp, err
}
if resp.StatusCode == 567 {
// HERE:
// Do something to prevent re-use of this connection
}
return resp, err
}

In gRPC, can the receiver of a unidirectional stream to cancel the stream and send an error to the sender?

In a gRPC unidirectional client-to-server stream, it is possible for the server to cancel the stream and return an error message to the client?
I've tried setting a trailer and returning a status message with .SendAndClose(), but neither are readable from the client. At the client, .Send returns an EOF error as expected, but .CloseAndRecv() does not return the status message sent by the server and .Trailer() returns an empty map.
// protobuf:
service Foo {
rpc Eat (stream Food) returns (Status) {}
}
// Server:
var Retval pb.Status
Retval.Status = "something went wrong"
emap := make(map[string]string)
emap["error"] = "something went wrong"
MD := metadata.New(emap)
Stream.SetTrailer(MD)
Stream.SendAndClose(&Retval)
// Client:
err = Stream.Send(Stuff) // returns EOF
if err != nil {
Status, err := o.Stream.CloseAndRecv() //returns nil, EOF
MD := o.Stream.Trailer() // returns an empty map.
}
Is there a way to do this without a bidirectional stream or a separate RPC endpoint for the client to request status messages from the server?
First, you don't need to define the Status object in your proto file. You can for example return a Well Known Type called Empty.
To do this you can do:
import "google/protobuf/empty.proto"
service Foo {
rpc Eat (stream Food) returns (google.protobuf.Empty);
}
This is just a recommendation because you don't need to provide a Status object. gRPC already has one defined that you can use in your go code.
Then on the server side you don't need to call SendAndClose when you want to return an error because your Eat function will have the following function declaration:
func (*Server) Eat(stream pb.Foo_EatServer) error
You can use something like the following code to send a status specifying that you had an error.
return nil, status.Errorf(
codes.Internal, // check suitable code
"A description for the error",
)
and before returning you can set a Trailer that will trigger error on the client side.
On the client side, you'll need to do something like:
trailer := stream.Trailer()
v, exist := trailer["error"]
if exist { // there is an error
fmt.Println("Error: ", v)
}
let me know if you need more help on that.

While sending request: Bad Request

I am taking use proxy to use POST method.
GET requests are successful, but there are problems with POST.
As I understand it, the POST request timed out, and therefore an error appears.
This is a proxy problem, or something I don't understand?
Code:
func main() {
WowProxy := getProxyFromWeb()
fmt.Println(WowProxy)
client := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(WowProxy)}}
req, err := client.PostForm("https://example.com", url.Values{"someKey" : {"SomeValue"}})
if err != nil{
panic(err.Error())
}
fmt.Println(req) }
Error:
panic: Post "https://example.com": Bad Request
Malformed request syntax, invalid request message framing, or deceptive request routing, would return a 400 Bad Request error.
400 Bad Request as per MDN

Is the handler suppose to populate content-type in http response header?

Below handler handles GET request, without populating http Response header:
// ListAll handles GET requests and returns all current products
func (p *ProductHandler) ListAll(rw http.ResponseWriter, r *http.Request) {
p.l.Println("[DEBUG] get all records")
prods := data.GetProducts()
err := data.ToJSON(prods, rw)
if err != nil {
// we should never be here but log the error just incase
p.l.Println("[ERROR] serializing product", err)
}
}
Below handler handles GET request, populating http Response header:
// ListAll handles GET requests and returns all current products
func (p *ProductHandler) ListAll(rw http.ResponseWriter, r *http.Request) {
p.l.Println("[DEBUG] get all records")
rw.Header().Add("Content-Type", "application/json")
prods := data.GetProducts()
err := data.ToJSON(prods, rw)
if err != nil {
// we should never be here but log the error just incase
p.l.Println("[ERROR] serializing product", err)
}
}
Both cases are working fine with simple curl request.
For any http client,
When do we need to populate content-type header, before sending the response, to client?
Always read the documentation first!
The answer to this is clearly covered here (emphasis obviously added):
// If WriteHeader has not yet been called, Write calls
// WriteHeader(http.StatusOK) before writing the data. If the Header
// does not contain a Content-Type line, Write adds a Content-Type set
// to the result of passing the initial 512 bytes of written data to
// DetectContentType. Additionally, if the total size of all written
// data is under a few KB and there are no Flush calls, the
// Content-Length header is added automatically.
To explicitly answer your secondary question:
When do we need to populate content-type header?
Any time you don't want it to be automatically detected. Automatic detection is imprecise, so you generally don't want to rely on it.

Empty HTTP Response Using http.Client.Do in Golang

I am using Go to make an HTTP GET request to an external web service. For some reason, the body of the response is always empty; the content length is always zero bytes. The response status code is always 200, however, and the call to Client.Do returns no error. The request requires an Authorization header, so I am using the http.NewRequest / http.Client.Do pattern to submit the request, as you'll see below. I have done requests similar to these in the past, but never using a GET that required a header. It seems unlikely that this the cause, but I wonder if it may be related. If anyone can spot any potential issues with the pattern used or perhaps has had a similar experience, I'd really appreciate any help.
Thank you.
if req, err := http.NewRequest("GET", "https://api.molt.in/v1/orders/11111111/items", nil); err != nil {
return nil, err
} else {
client := &http.Client{}
req.Header.Add("Authorization", "secretToken")
if resp, err := client.Do(req); err != nil {
return nil, err
} else {
defer resp.Body.Close()
return readBody(resp.Body)
}
}
I finally discovered the source of the problem. It had nothing to do with the request being made, or the response being received. It had to do with the parsing of the response.
I was using bufio.NewScanner.Text to attempt to convert the response body into a string. Replacing this call with one to ioutil.ReadAll output the string that I originally expected.
Thanks for all of your help, and apologies for the misleading question.

Resources