variable is empty but later has a value - go

I'm trying to develop a Terraform provider but I have a problem of the first request body. Here is the code:
type Body struct {
id string
}
func resourceServerCreate(d *schema.ResourceData, m interface{}) error {
key := d.Get("key").(string)
token := d.Get("token").(string)
workspace_name := d.Get("workspace_name").(string)
board_name := d.Get("board_name").(string)
resp, err := http.Post("https://api.trello.com/1/organizations?key="+key+"&token="+token+"&displayName="+workspace_name,"application/json",nil)
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
//lettura body.
body := new(Body)
json.NewDecoder(resp.Body).Decode(body)
log.Println("[ORCA MADONNA] il log funzia "+body.id)
d.Set("board_id",body.id)
resp1, err1 := http.Post("https://api.trello.com/1/boards?key="+key+"&token="+token+"&idOrganization="+body.id+"&=&name="+board_name,"application/json",nil)
if err1 != nil {
log.Fatalln(resp1)
}
defer resp1.Body.Close()
d.SetId(board_name)
return resourceServerRead(d, m)
}
In the log is empty, but the second call have it and work fine. How is it possible?

Go doesn't force you to check error responses, therefore it's easy to make silly mistakes. Had you checked the return value from Decode(), you would have immediately discovered a problem.
err := json.NewDecoder(resp.Body).Decode(body)
if err != nil {
log.Fatal("Decode error: ", err)
}
Decode error: json: Unmarshal(non-pointer main.Body)
So your most immediate fix is to use & to pass a pointer to Decode():
json.NewDecoder(resp.Body).Decode(&body)
Also of note, some programming editors will highlight this mistake for you:
Here's a working demonstration, including a corrected Body structure as described at json.Marshal(struct) returns “{}”:
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
type JSON = map[string]interface{}
type JSONArray = []interface{}
func ErrFatal(err error, msg string) {
if err != nil {
log.Fatal(msg+": ", err)
}
}
func handleTestRequest(w http.ResponseWriter, req *http.Request) {
w.Write(([]byte)("{\"id\":\"yourid\"}"))
}
func launchTestServer() {
http.HandleFunc("/", handleTestRequest)
go http.ListenAndServe(":8080", nil)
time.Sleep(1 * time.Second) // allow server to get started
}
// Medium: "Don’t use Go’s default HTTP client (in production)"
var restClient = &http.Client{
Timeout: time.Second * 10,
}
func DoREST(method, url string, headers, payload JSON) *http.Response {
requestPayload, err := json.Marshal(payload)
ErrFatal(err, "json.Marshal(payload")
request, err := http.NewRequest(method, url, bytes.NewBuffer(requestPayload))
ErrFatal(err, "NewRequest "+method+" "+url)
for k, v := range headers {
request.Header.Add(k, v.(string))
}
response, err := restClient.Do(request)
ErrFatal(err, "DoRest client.Do")
return response
}
type Body struct {
Id string `json:"id"`
}
func clientDemo() {
response := DoREST("POST", "http://localhost:8080", JSON{}, JSON{})
defer response.Body.Close()
var body Body
err := json.NewDecoder(response.Body).Decode(&body)
ErrFatal(err, "Decode")
fmt.Printf("Body: %#v\n", body)
}
func main() {
launchTestServer()
for i := 0; i < 5; i++ {
clientDemo()
}
}

Related

How to convert go type Cookie to type string

In go I have a function:
func UrlGET(url string, headers string) string { // inputs are url and headers for a http request
...
req, err := http.NewRequest("GET", url, nil)
...
resp, err := client.Do(req)
defer resp.Body.Close()
if rc := resp.Cookies(); len(rc) > 0 {
return string(rc)
}
return ""
}
However, you cannot convert type Cookie ([]*http.Cookie to type string (cannot convert rc (type []*http.Cookie) to type string). What would be an alternative or another way to convert to type string, ideally I would still return type string. I'm relatively new to go so at a bit of a wall as to what else to try.
Ideally, it would return like cookie=some_cookie_value as a string.
If you just want one big string, you can do:
package main
import "net/http"
func main() {
r, e := http.Get("https://stackoverflow.com")
if e != nil {
panic(e)
}
defer r.Body.Close()
s := r.Header.Get("Set-Cookie")
println(s)
}
Or you could build a map:
package main
import (
"fmt"
"net/http"
)
func main() {
r, e := http.Get("https://stackoverflow.com")
if e != nil {
panic(e)
}
defer r.Body.Close()
m := make(map[string]string)
for _, c := range r.Cookies() {
m[c.Name] = c.Value
}
fmt.Println(m)
}
https://golang.org/pkg/net/http#Response.Cookies
https://golang.org/pkg/net/http#Response.Header

cannot encode json.decoded request body

I have a server implementation. Now I am writing unit test to check it's functionalities.
I cannot prepare request, that would unmarshall on the server side well. Code below results with InvalidUnmarshallError. I don't know, how to debug it further.
Client side code:
body := PatchCatRequest{Adopted: true}
bodyBuf := &bytes.Buffer{}
err := json.NewEncoder(bodyBuf).Encode(body)
assert.NoError(t, err)
req, err := http.NewRequest("PATCH", URL+"/"+catId, bodyBuf)
recorder := httptest.NewRecorder()
handler.PatchCat(recorder, req.WithContext(ctx))
Server side code:
type PatchCatRequest struct {
Adopted bool `json:"adopted"`
}
func (h *Handler) PatchCat (rw http.ResponseWriter, req *http.Request) {
var patchRequest *PatchCatRequest
if err := json.NewDecoder(req.Body).Decode(patchRequest); err != nil {
rw.WriteHeader(http.StatusBadRequest)
logger.WithField("error", err.Error()).Error(ErrDocodeRequest.Error())
return
}
...
}
You are unmarshaling into a nil pointer, as the error message says:
package main
import (
"encoding/json"
"fmt"
)
type PatchCatRequest struct {
Adopted bool
}
func main() {
var patchRequest *PatchCatRequest // nil pointer
err := json.Unmarshal([]byte(`{"Adopted":true}`), patchRequest)
fmt.Println(err) // json: Unmarshal(nil *main.PatchCatRequest)
}
https://play.golang.org/p/vt7t5BgT3lA
Initialize the pointer before unmarshaling:
func main() {
patchRequest := new(PatchCatRequest) // non-nil pointer
err := json.Unmarshal([]byte(`{"Adopted":true}`), patchRequest)
fmt.Println(err) // <nil>
}
https://play.golang.org/p/BqliguktWmr

Poll API, pass result to chan, pass from chan to Websocket. Panic

I'm writing a small package which does a GET request to an external API every 2 seconds. It takes the value from this request and passes it into a channel. I have made this channel available to a http.handler (chi router) which upgrades to a websocket where the front-end will grab the value in realtime. the panic error is a lot of lines but i guess the most important is this:
2018/11/14 16:47:55 http: response.WriteHeader on hijacked connection
2018/11/14 16:47:55 http: response.Write on hijacked connection
Aside from that I'm sure there is a better way of doing this. Any experienced Gophers out there have any pointers to help a noob such as myself improve this?
package currencyticker
import (
"bitbucket.org/special/api/config"
"encoding/json"
"fmt"
"github.com/go-chi/chi"
"github.com/go-chi/render"
"github.com/gorilla/websocket"
"github.com/leekchan/accounting"
"io/ioutil"
"log"
"math/big"
"net/http"
"time"
)
var (
ac = accounting.Accounting{Precision: 2}
from = "USD"
to = "EUR,SWK"
url = "https://min-api.currencyapi.com/data/price?fsym=" + from + "&tsyms=" + to
messages = make(chan float64)
)
var wsupgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true // Disable CORS for testing
},
}
// Config - init
type Config struct {
*config.Config
}
type result map[string]float64
// New - init the configs
func New(configuration *config.Config) *Config {
return &Config{configuration}
}
// Routes - api urls
func (config *Config) Routes() *chi.Mux {
router := chi.NewRouter()
router.Use(
render.SetContentType(render.ContentTypeHTML), // Set content-Type headers as application/json
)
router.Get("/", config.GetPrice) // subscribe to new tweets
return router
}
func (config *Config) GetPrice(w http.ResponseWriter, r *http.Request) {
conn, err := wsupgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println(fmt.Printf("Failed to set websocket upgrade: %+v ", err))
return
}
for {
time.Sleep(1 * time.Second)
price := <-messages
w, err := conn.NextWriter(websocket.TextMessage)
if err != nil {
fmt.Println("ws error", err)
}
currVal := ac.FormatMoneyBigFloat(big.NewFloat(price))
if _, err := w.Write([]byte(currVal)); err != nil {
fmt.Printf("w.Write() returned %v", err)
}
w.Close()
}
}
// start getting the price of ether as soon as they ap starts
func init() {
go startPollingPriceAPI()
}
// Go Routine to start polling
func startPollingPriceAPI() {
for {
time.Sleep(2 * time.Second)
go getPriceFromAPI()
}
}
func getPriceFromAPI() {
w := http.Client{
// Timeout: time.Second * 3,
}
req, _ := http.NewRequest(http.MethodGet, url, nil)
res, err := w.Do(req)
if err != nil {
log.Println("err getting price [req]: ", err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Println("err getting price [io-read]: ", err)
}
r := result{}
if jsonErr := json.Unmarshal(body, &r); jsonErr != nil {
log.Println("err getting price [json]: ", jsonErr)
}
fmt.Println("1 Dollar = €", r["EUR"])
messages <- r["EUR"]
}

how to send dynamic json key/value as request params

package main
import (
"strings"
"net/http"
"encoding/json"
"fmt"
)
func main() {
j := `{"url":"http://localhost/test/take-request", "params":{"name":"John","age":"20"},"type":"get"}`
// k := `{"url":"http://localhost/test/take-request", "params":{"gender":"m","a":"20"},"type":"post"}`
request := map[string]interface{}{}
err := json.Unmarshal([]byte(j), &request)
if err != nil {
panic(err)
}
fmt.Println(request)
requestType = strings.ToUpper(request["type"]);
requestUrl = request["url"];
fmt.Println(request["params"])
// how do i get the keys and their values from params.
// note params is dynamic.
for _, v := range request["params"].(map[string]interface{}) {
// println(v)
switch t := v.(type) {
case string, []int:
fmt.Println(t)
default:
fmt.Println("wrong type")
}
}
sendRequest(requestType, requestUrl)
}
func sendRequest(type string, url string) string {
req, err := http.NewRequest(type, url, nil)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response Body:", string(body))
return string(body)
}
How do I iterate through params that is an interface
how do i get the key and its values
You can greatly simplify your code by using a proper struct for your JSON unmarshaling:
type Request struct {
URL string `json:"url"`
Params map[string]interface{} `json:"params"`
Type string `json:"type"`
}
Then you can unmarshal it more simply as so:
request := &Request{}
if err := json.Unmarshal([]byte(j), &request); err != nil {
panic(err)
}
And access the values as such:
requestType = request.Type
requestURL = request.URL
for key, value := range request.Params {
switch v := value.(type) {
case float64:
// handle numbers
case string:
// handle strings
}
}

How to read response body of ReverseProxy

package main
import (
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
target := &url.URL{Scheme: "http", Host: "www.google.com"}
proxy := httputil.NewSingleHostReverseProxy(target)
http.Handle("/google", proxy)
http.ListenAndServe(":8099", nil)
}
Reverse Proxy is works. How can I get the response body?
now httputil/reverseproxy, support than, see source
type ReverseProxy struct {
...
// ModifyResponse is an optional function that
// modifies the Response from the backend
// If it returns an error, the proxy returns a StatusBadGateway error.
ModifyResponse func(*http.Response) error
}
func rewriteBody(resp *http.Response) (err error) {
b, err := ioutil.ReadAll(resp.Body) //Read html
if err != nil {
return err
}
err = resp.Body.Close()
if err != nil {
return err
}
b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1) // replace html
body := ioutil.NopCloser(bytes.NewReader(b))
resp.Body = body
resp.ContentLength = int64(len(b))
resp.Header.Set("Content-Length", strconv.Itoa(len(b)))
return nil
}
// ...
target, _ := url.Parse("http://example.com")
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.ModifyResponse = rewriteBody
httputil.ReverseProxy has a Transport field. You can use it to modify the response. For example:
type transport struct {
http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = resp.Body.Close()
if err != nil {
return nil, err
}
b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1)
body := ioutil.NopCloser(bytes.NewReader(b))
resp.Body = body
resp.ContentLength = int64(len(b))
resp.Header.Set("Content-Length", strconv.Itoa(len(b)))
return resp, nil
}
// ...
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.Transport = &transport{http.DefaultTransport}
Playground example of the whole thing: http://play.golang.org/p/b0S5CbCMrI.
I don't know best solution. But you can do something like this:
package main
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
target := &url.URL{Scheme: "http", Host: "www.google.com"}
proxy := httputil.NewSingleHostReverseProxy(target)
http.Handle("/google", CustomHandler(proxy))
http.ListenAndServe(":8099", nil)
}
func CustomHandler(h http.Handler) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
h.ServeHTTP(NewCustomWriter(res), req)
}
}
type customWriter struct {
http.ResponseWriter
}
func NewCustomWriter(w http.ResponseWriter) *customWriter {
return &customWriter{w}
}
func (c *customWriter) Header() http.Header {
return c.ResponseWriter.Header()
}
func (c *customWriter) Write(data []byte) (int, error) {
fmt.Println(string(data)) //get response here
return c.ResponseWriter.Write(data)
}
func (c *customWriter) WriteHeader(i int) {
c.ResponseWriter.WriteHeader(i)
}
From source code httptest.ResponseRecorder is use for get the response from the handler
func TestModifyResponseClosesBody(t *testing.T) {
req, _ := http.NewRequest("GET", "http://foo.tld/", nil)
req.RemoteAddr = "1.2.3.4:56789"
closeCheck := new(checkCloser)
logBuf := new(bytes.Buffer)
outErr := errors.New("ModifyResponse error")
rp := &ReverseProxy{
Director: func(req *http.Request) {},
Transport: &staticTransport{&http.Response{
StatusCode: 200,
Body: closeCheck,
}},
ErrorLog: log.New(logBuf, "", 0),
ModifyResponse: func(*http.Response) error {
return outErr
},
}
rec := httptest.NewRecorder()
rp.ServeHTTP(rec, req)
res := rec.Result()
if g, e := res.StatusCode, http.StatusBadGateway; g != e {
t.Errorf("got res.StatusCode %d; expected %d", g, e)
}
if !closeCheck.closed {
t.Errorf("body should have been closed")
}
if g, e := logBuf.String(), outErr.Error(); !strings.Contains(g, e) {
t.Errorf("ErrorLog %q does not contain %q", g, e)
}
}

Resources