Golang *bytes.Buffer nil causes fatal error [closed] - http

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I had the same issue as https://github.com/golang/go/issues/26666 because I have a wrap function for my http requests.
Sometimes I need to request:
body := new(bytes.Buffer)
json.NewEncoder(body).Encode(h)
req("POST", "http://example.com", body)
And sometimes it's simply:
req("GET", "http://example.com", nil)
runtime error: invalid memory address or nil pointer dereference
I ended up with:
req("GET", "http://example.com", new(bytes.Buffer))
But I'm not sure if it's the right thing to do.
The function:
func req(method string, url string, body *bytes.Buffer) int {
req, err := http.NewRequest(method, url, body)
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(user, psw)
resp, err := client.Do(req)
checkErr(err)
if resp.StatusCode > 500 {
time.Sleep(30 * time.Second)
resp, err = client.Do(req)
checkErr(err)
}
defer resp.Body.Close()
return resp.StatusCode
}
Updated function:
func req(method string, url string, body io.Reader) int {
req, err := http.NewRequest(method, url, body)
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(user, psw)
resp, err := client.Do(req)
checkErr(err)
defer resp.Body.Close()
if resp.StatusCode >= 500 {
time.Sleep(30 * time.Second)
req, err := http.NewRequest(method, url, body)
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(user, psw)
resp, err := client.Do(req)
checkErr(err)
defer resp.Body.Close()
}
return resp.StatusCode
}
func checkErr(err error) {
if err != nil {
log.Fatal(err)
}
}

The body in http.NewRequest() is optional, so passing nil is acceptable when you're doing GET requests.
The problem is that the body parameter of http.NewRequest is an interface type: io.Reader, and you're attempting to pass a value of a concrete type *bytes.Buffer. What happens is that this nil pointer will be wrapped in a non-nil interface value, and that will be passed to http.NewRequest as the body.
If you don't have a body, pass nil explicitly, like this:
func req(method string, url string, body *bytes.Buffer) int {
var bodyToPass io.Reader
if body != nil {
bodyToPass = body
}
req, err := http.NewRequest(method, url, bodyToPass)
// ....
}
And then you can call it like:
req("GET", "http://example.com", nil)
Although best would be if your req() function would take io.Reader in the first place, so you don't have to check its value explicitly:
func req(method string, url string, body io.Reader) int {
req, err := http.NewRequest(method, url, body) // You may pass it as-is
// ....
}
And you can call it with nil or with a non-nil *bytes.Buffer too:
req("GET", "http://example.com", nil) // OK
req("POST", "http://example.com", bytes.NewBufferString("data")) // Also OK
For more details, see Hiding nil values, understanding why golang fails here

Related

Go http request redirect

I am writing an API whichs has to redirect incoming requests to another service, the response must be forwarded to the original requester.
I figured a simple function like below should do the trick, but I was wrong.
I receive the data from my redirected response, however when I send it back to the initial request I receive this response without any data Could not get response. Error: socket hang up
If I try to execute the very same request using postman straight to the redirect URL it works perfectly fine.
func initialAssetsHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
return
}
resp, err := http.Post(conf.redirectURL, "application/json", bytes.NewReader(body))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
log.Info(string(buf.Bytes()))
var data json.RawMessage
if err = json.NewDecoder(resp.Body).Decode(&data); err != nil {
fmt.Println(err)
return
}
helper.SendJsonRaw(w, 200, data)
}
Here is the SendJsonRaw function:
func SendJsonRaw(w http.ResponseWriter, status int, r json.RawMessage) error {
w.Header().Set(HeaderContentType, MimeApplicationJSON)
w.WriteHeader(status)
_, err := w.Write(r)
return err
}
The r.Body is read by the json decoder up to EOF, then when you pass it to the redirect request it looks empty to the http.Client and therefore it sends no body. You need to retain the content of the body.
For example you can do the following:
func initialAssetsHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
return
}
var initialAssets TagAssets
if err := json.Unmarshal(&initialAssets, body); err != nil {
if !strings.Contains(err.Error(), "json: invalid use of ,string struct tag, trying to unmarshal") {
helper.SendJsonError(w, http.StatusBadRequest, err)
return
}
}
resp, err := http.Post(conf.redirectURL, "application/json", bytes.NewReader(body))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
log.Info(resp)
var data json.RawMessage
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
fmt.Println(err)
return
}
helper.SendJsonOk(w, data)
}

Sending data in Chunks using single HTTP Post connection

I receive the contents of a file from a data source in chunks. As and when I receive the chunk I want to send the chunk data to a service using http POST request. And by keeping alive the same http POST connection used for sending the first chunk I want to send the remaining chunks of data.
I came up with the following code snippet to implement something similar.
Server-Side
func handle(w http.ResponseWriter, req *http.Request) {
buf := make([]byte, 256)
var n int
for {
n, err := req.Body.Read(buf)
if n == 0 && err == io.EOF {
break
}
fmt.Printf(string(buf[:n]))
}
fmt.Printf(string(buf[:n]))
fmt.Printf("Transfer Complete")
}
Client-Side
type alphaReader struct {
reader io.Reader
}
func newAlphaReader(reader io.Reader) *alphaReader {
return &alphaReader{reader: reader}
}
func (a *alphaReader) Read(p []byte) (int, error) {
n, err := a.reader.Read(p)
return n, err
}
func (a *alphaReader) Reset(str string) {
a.reader = strings.NewReader(str)
}
func (a *alphaReader) Close() error {
return nil
}
func main() {
tr := http.DefaultTransport
alphareader := newAlphaReader(strings.NewReader("First Chunk"))
client := &http.Client{
Transport: tr,
Timeout: 0,
}
req := &http.Request{
Method: "POST",
URL: &url.URL{
Scheme: "http",
Host: "localhost:8080",
Path: "/upload",
},
ProtoMajor: 1,
ProtoMinor: 1,
ContentLength: -1,
Body: alphareader,
}
fmt.Printf("Doing request\n")
_, err := client.Do(req)
alphareader.Reset("Second Chunk")
fmt.Printf("Done request. Err: %v\n", err)
}
Here I want that when I do alphareader.Reset("Second Chunk"), the string "Second Chunk" should be sent using the POST connection made earlier. But that is not happening. The connection gets closed after sending the First Chunk of data. Also I have not written the Close() method properly which I'm not sure how to implement.
I'm newbie to golang and any suggestions would be greatly helpful regarding the same.
A *strings.Reader returns io.EOF after the initial string has been read and your wrapper does nothing to change that, so it cannot be reused. You're looking for io.Pipe to turn the request body into an io.Writer.
package main
import (
"io"
"net/http"
)
func main() {
pr, pw := io.Pipe()
req, err := http.NewRequest("POST", "http://localhost:8080/upload", pr)
if err != nil {
// TODO: handle error
}
go func() {
defer pw.Close()
if _, err := io.WriteString(pw, "first chunk"); err != nil {
_ = err // TODO: handle error
}
if _, err := io.WriteString(pw, "second chunk"); err != nil {
_ = err // TODO: handle error
}
}()
res, err := http.DefaultClient.Do(req)
if err != nil {
// TODO: handle error
}
res.Body.Close()
}
Also, don't initialize the request using a struct literal. Use one of the constructors instead. In your code you're not setting the Host and Header fields, for instance.

Convert string from args/flags to json then send to post http request as body

I'm trying to pass the values ​​I get in the arg / flag --BODY="{user: root}" as the body of the post request I'm trying to do, I've already used json.Marshal and even so I wasn't successful, thanks for the help !
Code below:
func Custom(method string, url string, token string, data string) {
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
req.Header.Add("Authorization", FormatBearerToken(token))
// req.Header.Set("Content-Type", "application/json")
if err != nil {
log.Println("Request failed, ", err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Println(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
log.Println("response Body:", string(body))
}

How to access http client response body global [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
In the code below, am trying to access the variable "Regprofile" as global variable but and getting empty output. Any help?
type GMLCInstance struct {
NfInstanceID string `json:"nfInstanceID"`
HeartBeatTimer int `json:"heartBeatTimer"`
NfType []string `json:"nfType"`
NfStatus []string `json:"nfStatus"`
Ipv4Addresses []string `json:"ipv4Addresses"`
}
var Regprofile GMLCInstance
// Request credentials and token from NRF and register profile to NFR database
func init() {
urlcred := "https://127.0.0.1:9090/credentials"
// Perform the request
resp, err := netClient.Get(urlcred)
if err != nil {
log.Fatalf("Failed get: %s", err)
}
defer resp.Body.Close()
// Fill the record with the data from the JSON
var cr Credential
// Use json.Decode for reading streams of JSON data
if err := json.NewDecoder(resp.Body).Decode(&cr); err != nil {
log.Println(err)
}
//fmt.Println(cr)
clientId := cr.CLIENTID
clientsec := cr.CLIENTSECRET
// Get token
reqtoken := url.Values{
"grant_type": []string{"client_credentials"},
"client_id": []string{clientId},
"client_secret": []string{clientsec},
"scope": []string{"GMLC"},
}
urlq := "https://127.0.0.1:9090/oauth2/token?"
res, err := netClient.PostForm(urlq+reqtoken.Encode(), nil)
if err != nil {
log.Fatalf("Failed get: %s", err)
}
var auth AccessToken
// Use json.Decode for reading streams of JSON data
if err := json.NewDecoder(res.Body).Decode(&auth); err != nil {
log.Println(err)
}
//fmt.Println(auth.AccessToken)
token := auth.AccessToken
para := url.Values{
"access_token": []string{token},
}
//============================================================
// Register GMLC Instance to NRF, PUT
var gmlc = []byte(`{
"nfInstanceID": "f81d4fae-7dec-11d0-a765-00a0c91egmlc",
"heartBeatTimer": 0,
"nfType": ["GMLC"],
"nfStatus": ["string"],
"ipv4Addresses": ["172.16.0.X:5000"]
}`)
//================= Registser profile to NRF ========================//
postnrf := "https://127.0.0.1:9090/nnrf-nfm/v1/nf-instances/f81d4fae-7dec-11d0-a765-00a0c91egmlc?"
rq, err := http.NewRequest("PUT", postnrf+para.Encode(), bytes.NewBuffer(gmlc))
rq.Header.Set("Content-Type", "application/json")
rq.Header.Set("Accept", "application/json")
response, err := netClient.Do(rq)
if err != nil {
panic(err)
}
defer res.Body.Close()
decoder := json.NewDecoder(response.Body)
var Regprofile GMLCInstance
err = decoder.Decode(&Regprofile)
if err != nil {
fmt.Println("Response body not well decoded")
}
fmt.Println(Regprofile)
}
The output of fmt.Println(Regprofile) gives
{f81d4fae-7dec-11d0-a765-00a0c91egmlc 10 [GMLC] [REGISTERED] [172.16.0.X:5000]}
However, when i print the variable in for example main as
func main(){
fmt.Println(Regprofile)
}
I get empty data as
{ 0 [] [] []}
In func init() you redeclare variables var Regprofile GMLCInstance locally to function scope. This declaration shadows global variable with local one. Just delete this local declaration inside init().

Use same headers for every request

I want to be able to use the same headers for every request made by an HTTP client without having to specify them for every request like so:
req, err := http.NewRequest("GET", fmt.Sprintf("https://%s", endpoint), nil)
if err != nil {
log.Printf("Error making request to endpoint: %+v", err)
return p, err
}
req.Header.Add("Authorization", "Bearer")
req.Header.Add("Version", "2017-11-23")
resp, err := client.Do(req)
Is there a way to configure this on the client?
Use a function to encapsulate the code to create and configure the request:
func newRequest(endpoint string) (*http.Request, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("https://%s", endpoint), nil)
if err != nil {
return nil, err
}
req.Header.Add("Authorization", "Bearer")
req.Header.Add("Version", "2017-11-23")
return req, nil
}
A more complicated approach is to implement a round tripper that adds the headers and delegates to another round tripper:
type transport struct {
underlyingTransport http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Add("Authorization", "Bearer")
req.Header.Add("Version", "2017-11-23")
return t.underlyingTransport.RoundTrip(req)
}
Use it to create a client like this:
c := http.Client{Transport: &transport{ underlyingTransport: http.DefaultTransport } }
The transport adds the headers on calls to c.Do(), c.Get(), c.Post(), etc.

Resources