txn: don't try to decode request bodies > raft.SuggestedMaxDataSize (#6422)

txn: don't try to decode request bodies > raft.SuggestedMaxDataSize
This commit is contained in:
Sarah Adams 2019-08-30 10:41:25 -07:00 committed by GitHub
parent 70a5279114
commit 2254633d93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 0 deletions

View File

@ -4,6 +4,7 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"strings" "strings"
"time" "time"
@ -98,6 +99,18 @@ func isWrite(op api.KVOp) bool {
// a boolean, that if false means an error response has been generated and // a boolean, that if false means an error response has been generated and
// processing should stop. // processing should stop.
func (s *HTTPServer) convertOps(resp http.ResponseWriter, req *http.Request) (structs.TxnOps, int, bool) { func (s *HTTPServer) convertOps(resp http.ResponseWriter, req *http.Request) (structs.TxnOps, int, bool) {
sizeStr := req.Header.Get("Content-Length")
if sizeStr != "" {
if size, err := strconv.Atoi(sizeStr); err != nil {
fmt.Fprintf(resp, "Failed to parse Content-Length: %v", err)
return nil, 0, false
} else if size > int(s.agent.config.KVMaxValueSize) {
fmt.Fprintf(resp, "Request body too large, max size: %v bytes", s.agent.config.KVMaxValueSize)
return nil, 0, false
}
}
// Note the body is in API format, and not the RPC format. If we can't // Note the body is in API format, and not the RPC format. If we can't
// decode it, we will return a 400 since we don't have enough context to // decode it, we will return a 400 since we don't have enough context to
// associate the error with a given operation. // associate the error with a given operation.

View File

@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"reflect" "reflect"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -602,3 +603,50 @@ func TestTxnEndpoint_UpdateCheck(t *testing.T) {
} }
verify.Values(t, "", txnResp, expected) verify.Values(t, "", txnResp, expected)
} }
func TestConvertOps_ContentLength(t *testing.T) {
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
jsonBody := `[
{
"KV": {
"Verb": "set",
"Key": "key1",
"Value": "aGVsbG8gd29ybGQ="
}
}
]`
tests := []struct {
contentLength string
ok bool
}{
{"", true},
{strconv.Itoa(len(jsonBody)), true},
{strconv.Itoa(raft.SuggestedMaxDataSize), true},
{strconv.Itoa(raft.SuggestedMaxDataSize + 100), false},
}
for _, tc := range tests {
t.Run("contentLength: "+tc.contentLength, func(t *testing.T) {
resp := httptest.NewRecorder()
var body bytes.Buffer
// Doesn't matter what the request body size actually is, as we only
// check 'Content-Length' header in this test anyway.
body.WriteString(jsonBody)
req := httptest.NewRequest("POST", "http://foo.com", &body)
req.Header.Add("Content-Length", tc.contentLength)
_, _, ok := a.srv.convertOps(resp, req)
if ok != tc.ok {
t.Fatal("ok != tc.ok")
}
})
}
}