Skip to content

Commit ac84ebe

Browse files
committed
Add debbugging and fixes which may cause body to be read improperly
1 parent 000df72 commit ac84ebe

File tree

4 files changed

+60
-17
lines changed

4 files changed

+60
-17
lines changed

gateway/coprocess.go

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ type CoProcessor struct {
7272

7373
// BuildObject constructs a CoProcessObject from a given http.Request.
7474
func (c *CoProcessor) BuildObject(req *http.Request, res *http.Response, spec *APISpec) (*coprocess.Object, error) {
75+
logger := log.WithFields(logrus.Fields{
76+
"prefix": "coprocess",
77+
"func": "BuildObject",
78+
})
79+
7580
headers := ProtoMap(req.Header)
7681

7782
host := req.Host
@@ -103,15 +108,39 @@ func (c *CoProcessor) BuildObject(req *http.Request, res *http.Response, spec *A
103108
}
104109

105110
if req.Body != nil {
106-
defer req.Body.Close()
107111
var err error
108-
miniRequestObject.RawBody, err = ioutil.ReadAll(req.Body)
112+
contentLength := req.ContentLength
113+
logger.Debugf("Request body processing - Content-Length header: %d", contentLength)
114+
115+
// Check if body is empty (might have been read already)
116+
emptyBody := false
117+
if req.GetBody == nil {
118+
logger.Debug("Request GetBody is nil, body might have been read already")
119+
emptyBody = true
120+
}
121+
122+
body, _ := copyBody(req.Body, false)
123+
bodyBytes, err := ioutil.ReadAll(body)
124+
bytesRead := len(bodyBytes)
125+
109126
if err != nil {
110-
return nil, err
111-
}
127+
logger.WithError(err).Errorf("Failed to read request body - Content-Length: %d, Bytes read: %d, Empty body: %v",
128+
contentLength, bytesRead, emptyBody)
129+
return nil, fmt.Errorf("failed to read request body: %w (Content-Length: %d, Bytes read: %d)",
130+
err, contentLength, bytesRead)
131+
}
132+
133+
logger.Debugf("Successfully read request body - Content-Length: %d, Bytes read: %d",
134+
contentLength, bytesRead)
135+
136+
body.Close()
137+
138+
miniRequestObject.RawBody = bodyBytes
112139
if utf8.Valid(miniRequestObject.RawBody) && !c.Middleware.RawBodyOnly {
113140
miniRequestObject.Body = string(miniRequestObject.RawBody)
114141
}
142+
} else {
143+
logger.Debug("Request body is nil")
115144
}
116145

117146
object := &coprocess.Object{
@@ -175,10 +204,22 @@ func (c *CoProcessor) BuildObject(req *http.Request, res *http.Response, spec *A
175204
resObj.MultivalueHeaders = append(resObj.MultivalueHeaders, &currentHeader)
176205
}
177206
resObj.StatusCode = int32(res.StatusCode)
207+
contentLength := res.ContentLength
208+
logger.Debugf("Response body processing - Content-Length header: %d", contentLength)
209+
178210
rawBody, err := ioutil.ReadAll(res.Body)
211+
bytesRead := len(rawBody)
212+
179213
if err != nil {
180-
return nil, err
181-
}
214+
logger.WithError(err).Errorf("Failed to read response body - Content-Length: %d, Bytes read: %d",
215+
contentLength, bytesRead)
216+
return nil, fmt.Errorf("failed to read response body: %w (Content-Length: %d, Bytes read: %d)",
217+
err, contentLength, bytesRead)
218+
}
219+
220+
logger.Debugf("Successfully read response body - Content-Length: %d, Bytes read: %d",
221+
contentLength, bytesRead)
222+
182223
resObj.RawBody = rawBody
183224
res.Body = ioutil.NopCloser(bytes.NewReader(rawBody))
184225
if utf8.Valid(rawBody) && !c.Middleware.RawBodyOnly {

gateway/coprocess_id_extractor.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,12 @@ func (e *BaseExtractor) ExtractForm(r *http.Request, paramName string) (formValu
6868

6969
// ExtractBody is used when BodySource is specified.
7070
func (e *BaseExtractor) ExtractBody(r *http.Request) (string, error) {
71-
body, err := ioutil.ReadAll(r.Body)
71+
body, _ := copyBody(r.Body, false)
72+
bodyBytes, err := ioutil.ReadAll(body)
7273
if err != nil {
7374
return "", err
7475
}
75-
return string(body), err
76+
return string(bodyBytes), err
7677
}
7778

7879
// Error is a helper for logging the extractor errors. It always returns HTTP 400 (so we don't expose any details).

gateway/handler_success.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ func recordGraphDetails(rec *analytics.AnalyticsRecord, r *http.Request, resp *h
129129
if r.Body == nil {
130130
return
131131
}
132-
body, err := io.ReadAll(r.Body)
132+
reqBody, _ := copyBody(r.Body, false)
133+
body, err := io.ReadAll(reqBody)
133134
defer func() {
134135
_ = r.Body.Close()
135136
r.Body = io.NopCloser(bytes.NewBuffer(body))

gateway/mw_transform.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,30 +52,30 @@ func (t *TransformMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Requ
5252
}
5353

5454
func transformBody(r *http.Request, tmeta *TransformSpec, t *TransformMiddleware) error {
55-
body, _ := ioutil.ReadAll(r.Body)
56-
defer r.Body.Close()
55+
body, _ := copyBody(r.Body, false)
56+
bodyBytes, _ := ioutil.ReadAll(body)
5757

5858
// Put into an interface:
5959
bodyData := make(map[string]interface{})
6060

6161
switch tmeta.TemplateData.Input {
6262
case apidef.RequestXML:
63-
if len(body) == 0 {
64-
body = []byte("<_/>")
63+
if len(bodyBytes) == 0 {
64+
bodyBytes = []byte("<_/>")
6565
}
6666
mxj.XmlCharsetReader = WrappedCharsetReader
6767
var err error
68-
bodyData, err = mxj.NewMapXml(body) // unmarshal
68+
bodyData, err = mxj.NewMapXml(bodyBytes) // unmarshal
6969
if err != nil {
7070
return fmt.Errorf("error unmarshalling XML: %w", err)
7171
}
7272
case apidef.RequestJSON:
73-
if len(body) == 0 {
74-
body = []byte("{}")
73+
if len(bodyBytes) == 0 {
74+
bodyBytes = []byte("{}")
7575
}
7676

7777
var tempBody interface{}
78-
if err := json.Unmarshal(body, &tempBody); err != nil {
78+
if err := json.Unmarshal(bodyBytes, &tempBody); err != nil {
7979
return err
8080
}
8181

0 commit comments

Comments
 (0)