Repository: getkin/kin-openapi
Pull request #672 (2022-11-18 18:50:53)

Update content length after replacing request body
Update content length after replacing the request body when setting default values. Otherwise, the request gets inconsistent, and further operation on it, like proxying it, causes an error.

fenollp: Hey thanks for this. Please add a regression test. Then I'll merge this. 
borodiychuk: @fenollp thanks, added tests.
fenollp: Thanks!

Patch:
diff --git a/openapi3filter/validate_request.go b/openapi3filter/validate_request.go
index 4b0bd34..beb47aa 100644
--- a/openapi3filter/validate_request.go
+++ b/openapi3filter/validate_request.go
@@ -285,6 +285,7 @@ func ValidateRequestBody(ctx context.Context, input *RequestValidationInput, req
 		}
 		// Put the data back into the input
 		req.Body = ioutil.NopCloser(bytes.NewReader(data))
+		req.ContentLength = int64(len(data))
 	}
 
 	return nil


Test patch:
diff --git a/openapi3filter/validate_request_test.go b/openapi3filter/validate_request_test.go
index 957b692..450ee59 100644
--- a/openapi3filter/validate_request_test.go
+++ b/openapi3filter/validate_request_test.go
@@ -59,6 +59,9 @@ paths:
               properties:
                 subCategory:
                   type: string
+                category:
+                  type: string
+                  default: Sweets
       responses:
         '201':
           description: Created
@@ -95,6 +98,7 @@ components:
 
 	type testRequestBody struct {
 		SubCategory string `json:"subCategory"`
+		Category    string `json:"category,omitempty"`
 	}
 	type args struct {
 		requestBody *testRequestBody
@@ -102,18 +106,30 @@ components:
 		apiKey      string
 	}
 	tests := []struct {
-		name        string
-		args        args
-		expectedErr error
+		name                 string
+		args                 args
+		expectedModification bool
+		expectedErr          error
 	}{
 		{
-			name: "Valid request",
+			name: "Valid request with all fields set",
+			args: args{
+				requestBody: &testRequestBody{SubCategory: "Chocolate", Category: "Food"},
+				url:         "/category?category=cookies",
+				apiKey:      "SomeKey",
+			},
+			expectedModification: false,
+			expectedErr:          nil,
+		},
+		{
+			name: "Valid request without certain fields",
 			args: args{
 				requestBody: &testRequestBody{SubCategory: "Chocolate"},
 				url:         "/category?category=cookies",
 				apiKey:      "SomeKey",
 			},
-			expectedErr: nil,
+			expectedModification: true,
+			expectedErr:          nil,
 		},
 		{
 			name: "Invalid operation params",
@@ -122,7 +138,8 @@ components:
 				url:         "/category?invalidCategory=badCookie",
 				apiKey:      "SomeKey",
 			},
-			expectedErr: &RequestError{},
+			expectedModification: false,
+			expectedErr:          &RequestError{},
 		},
 		{
 			name: "Invalid request body",
@@ -131,7 +148,8 @@ components:
 				url:         "/category?category=cookies",
 				apiKey:      "SomeKey",
 			},
-			expectedErr: &RequestError{},
+			expectedModification: false,
+			expectedErr:          &RequestError{},
 		},
 		{
 			name: "Invalid security",
@@ -140,7 +158,8 @@ components:
 				url:         "/category?category=cookies",
 				apiKey:      "",
 			},
-			expectedErr: &SecurityRequirementsError{},
+			expectedModification: false,
+			expectedErr:          &SecurityRequirementsError{},
 		},
 		{
 			name: "Invalid request body and security",
@@ -149,16 +168,19 @@ components:
 				url:         "/category?category=cookies",
 				apiKey:      "",
 			},
-			expectedErr: &SecurityRequirementsError{},
+			expectedModification: false,
+			expectedErr:          &SecurityRequirementsError{},
 		},
 	}
 	for _, tc := range tests {
 		t.Run(tc.name, func(t *testing.T) {
 			var requestBody io.Reader
+			var originalBodySize int
 			if tc.args.requestBody != nil {
 				testingBody, err := json.Marshal(tc.args.requestBody)
 				require.NoError(t, err)
 				requestBody = bytes.NewReader(testingBody)
+				originalBodySize = len(testingBody)
 			}
 			req, err := http.NewRequest(http.MethodPost, tc.args.url, requestBody)
 			require.NoError(t, err)
@@ -180,6 +202,16 @@ components:
 			}
 			err = ValidateRequest(context.Background(), validationInput)
 			assert.IsType(t, tc.expectedErr, err, "ValidateRequest(): error = %v, expectedError %v", err, tc.expectedErr)
+			if tc.expectedErr != nil {
+				return
+			}
+			body, err := io.ReadAll(validationInput.Request.Body)
+			contentLen := int(validationInput.Request.ContentLength)
+			bodySize := len(body)
+			assert.NoError(t, err, "unable to read request body: %v", err)
+			assert.Equal(t, contentLen, bodySize, "expect ContentLength %d to equal body size %d", contentLen, bodySize)
+			bodyModified := originalBodySize != bodySize
+			assert.Equal(t, bodyModified, tc.expectedModification, "expect request body modification happened: %t, expected %t", bodyModified, tc.expectedModification)
 		})
 	}
 }