Skip to content

Commit b1f98c6

Browse files
authored
fix: Make Go SDK set correct content-type header (#1283)
This change makes Go SDK set Content-Type header according to the body's type. Either text/plain if it is a string, or application/json if it is JSON formatted string. The oauth2 transport automatically sets a url encoded content type header for login call. This change will fix requests 500'ing on % being in the json formatted body. Fixes issue #1075
1 parent f3c1b2c commit b1f98c6

File tree

2 files changed

+98
-33
lines changed

2 files changed

+98
-33
lines changed

go/rtl/auth.go

+33-33
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,46 @@ func (s *AuthSession) Do(result interface{}, method, ver, path string, reqPars m
9191
// prepare URL
9292
u := fmt.Sprintf("%s/api%s%s", s.Config.BaseUrl, ver, path)
9393

94-
bodyString := serializeBody(body)
94+
bodyString := ""
95+
contentTypeHeader := ""
96+
97+
// serialize body to string and determine request's Content-Type header
98+
if body != nil {
99+
// get the `body`'s type
100+
kind := reflect.TypeOf(body).Kind()
101+
value := reflect.ValueOf(body)
102+
103+
// check if it is pointer
104+
if kind == reflect.Ptr {
105+
// if so find the type it points to
106+
kind = reflect.ValueOf(body).Elem().Kind()
107+
value = reflect.ValueOf(body).Elem()
108+
}
109+
110+
if kind == reflect.String {
111+
contentTypeHeader = "text/plain"
112+
bodyString = fmt.Sprintf("%v", value)
113+
} else {
114+
contentTypeHeader = "application/json"
115+
serializedBody, err := json.Marshal(body)
116+
117+
if err != nil {
118+
_, _ = fmt.Fprintf(os.Stderr, "error serializing body: %v", err)
119+
}
120+
121+
bodyString = string(serializedBody)
122+
}
123+
}
95124

96125
// create new request
97126
req, err := http.NewRequest(method, u, bytes.NewBufferString(bodyString))
98127
if err != nil {
99128
return err
100129
}
101130

131+
// set content-type header
132+
req.Header.Add("Content-Type", contentTypeHeader)
133+
102134
// set query params
103135
setQuery(req.URL, reqPars)
104136

@@ -138,38 +170,6 @@ func (s *AuthSession) Do(result interface{}, method, ver, path string, reqPars m
138170
return nil
139171
}
140172

141-
// serializeBody serializes body to a json, if the body is already string, it will just return it unchanged
142-
func serializeBody(body interface{}) string {
143-
ret := ""
144-
if body == nil {
145-
return ret
146-
}
147-
148-
// get the `body` type
149-
kind := reflect.TypeOf(body).Kind()
150-
value := reflect.ValueOf(body)
151-
152-
// check if it is pointer
153-
if kind == reflect.Ptr {
154-
// if so, use the value kind
155-
kind = reflect.ValueOf(body).Elem().Kind()
156-
value = reflect.ValueOf(body).Elem()
157-
}
158-
159-
// it is string, return it as it is
160-
if kind == reflect.String {
161-
return fmt.Sprintf("%v", value)
162-
}
163-
164-
bb, err := json.Marshal(body)
165-
if err != nil {
166-
_, _ = fmt.Fprintf(os.Stderr, "error serializing body: %v", err)
167-
}
168-
169-
return string(bb)
170-
171-
}
172-
173173
func isStringType(v interface{}) bool {
174174
return reflect.ValueOf(v).Kind() == reflect.String
175175
}

go/rtl/auth_test.go

+65
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,71 @@ func TestAuthSession_Do_Parse(t *testing.T) {
388388
})
389389
}
390390

391+
func TestAuthSession_Do_Content_Type(t *testing.T) {
392+
const path = "/someMethod"
393+
const apiVersion = "/4.0"
394+
395+
t.Run("Do() sets Content-Type header to 'application/json' if body is json", func(t *testing.T) {
396+
mux := http.NewServeMux()
397+
setupApi40Login(mux, foreverValidTestToken, http.StatusOK)
398+
server := httptest.NewServer(mux)
399+
defer server.Close()
400+
401+
mux.HandleFunc("/api"+apiVersion+path, func(w http.ResponseWriter, r *http.Request) {
402+
contentTypeHeader := r.Header.Get("Content-Type")
403+
expectedHeader := "application/json"
404+
if contentTypeHeader != expectedHeader {
405+
t.Errorf("Content-Type header not correct. got=%v want=%v", contentTypeHeader, expectedHeader)
406+
}
407+
})
408+
409+
s := NewAuthSession(ApiSettings{
410+
BaseUrl: server.URL,
411+
ApiVersion: apiVersion,
412+
})
413+
414+
var r string
415+
body := struct {
416+
key string
417+
}{
418+
key: "value",
419+
}
420+
421+
err := s.Do(&r, "GET", apiVersion, path, nil, body, nil)
422+
423+
if err != nil {
424+
t.Errorf("Do() call failed: %v", err)
425+
}
426+
})
427+
428+
t.Run("Do() sets Content-Type header to 'text/plain' if body is a string", func(t *testing.T) {
429+
mux := http.NewServeMux()
430+
setupApi40Login(mux, foreverValidTestToken, http.StatusOK)
431+
server := httptest.NewServer(mux)
432+
defer server.Close()
433+
434+
mux.HandleFunc("/api"+apiVersion+path, func(w http.ResponseWriter, r *http.Request) {
435+
contentTypeHeader := r.Header.Get("Content-Type")
436+
expectedHeader := "text/plain"
437+
if contentTypeHeader != expectedHeader {
438+
t.Errorf("Content-Type header not correct. got=%v want=%v", contentTypeHeader, expectedHeader)
439+
}
440+
})
441+
442+
s := NewAuthSession(ApiSettings{
443+
BaseUrl: server.URL,
444+
ApiVersion: apiVersion,
445+
})
446+
447+
var r string
448+
err := s.Do(&r, "GET", apiVersion, path, nil, "body", nil)
449+
450+
if err != nil {
451+
t.Errorf("Do() call failed: %v", err)
452+
}
453+
})
454+
}
455+
391456
func TestSetQuery(t *testing.T) {
392457
somestring := "somestring"
393458
testcases := []struct {

0 commit comments

Comments
 (0)