Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ func CreateServer(
rateLimitTimeWindowInSeconds int,
isProfileModeActivated bool,
shouldStartSwaggerUI bool,
maxRequestBodySize int64,
) (*http.Server, error) {
ws := gin.Default()
if maxRequestBodySize > 0 {
ws.Use(maxRequestBodySizeMiddleware(maxRequestBodySize))
}
ws.Use(cors.Default())

err := registerValidators()
Expand All @@ -61,6 +65,13 @@ func CreateServer(
return httpServer, nil
}

func maxRequestBodySizeMiddleware(maxSize int64) gin.HandlerFunc {
return func(c *gin.Context) {
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxSize)
c.Next()
}
}

func registerValidators() error {
validators := []validatorInput{
{Name: "skValidator", Validator: skValidator},
Expand Down
64 changes: 64 additions & 0 deletions api/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package api

import (
"bytes"
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)

func TestMaxRequestBodySizeMiddleware_OversizedBodyShouldErr(t *testing.T) {
t.Parallel()

ws := gin.New()
ws.Use(maxRequestBodySizeMiddleware(1 << 20))
ws.POST("/test", func(c *gin.Context) {
_, err := io.ReadAll(c.Request.Body)
if err != nil {
c.Status(http.StatusRequestEntityTooLarge)
return
}
c.Status(http.StatusOK)
})

hugeBody := make([]byte, 2*1024*1024)
for i := range hugeBody {
hugeBody[i] = 'a'
}

req := httptest.NewRequest(http.MethodPost, "/test", bytes.NewReader(hugeBody))
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

require.Equal(t, http.StatusRequestEntityTooLarge, resp.Code)
}

func TestMaxRequestBodySizeMiddleware_NormalBodyShouldPass(t *testing.T) {
t.Parallel()

ws := gin.New()
ws.Use(maxRequestBodySizeMiddleware(1 << 20))
ws.POST("/test", func(c *gin.Context) {
body, err := io.ReadAll(c.Request.Body)
if err != nil {
c.Status(http.StatusInternalServerError)
return
}
if string(body) != `{"key":"value"}` {
c.Status(http.StatusBadRequest)
return
}
c.Status(http.StatusOK)
})

body := []byte(`{"key":"value"}`)
req := httptest.NewRequest(http.MethodPost, "/test", bytes.NewReader(body))
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

require.Equal(t, http.StatusOK, resp.Code)
}
3 changes: 3 additions & 0 deletions cmd/proxy/config/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
# TimeBetweenNodesRequestsInSec represents time to wait before retry to get the number of shards from observers
TimeBetweenNodesRequestsInSec = 2

# MaxRequestBodySize represents the maximum request body size in bytes for all API endpoints
MaxRequestBodySize = 10000000 # 10 MB

[AddressPubkeyConverter]
#Length specifies the length in bytes of an address
Length = 32
Expand Down
1 change: 1 addition & 0 deletions cmd/proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ func startWebServer(
generalConfig.GeneralSettings.RateLimitWindowDurationSeconds,
isProfileModeActivated,
shouldStartSwaggerUI,
generalConfig.GeneralSettings.MaxRequestBodySize,
)

if err != nil {
Expand Down
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type GeneralSettingsConfig struct {
AllowEntireTxPoolFetch bool
NumShardsTimeoutInSec int
TimeBetweenNodesRequestsInSec int
MaxRequestBodySize int64
}

// Config will hold the whole config file's data
Expand Down
1 change: 1 addition & 0 deletions data/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ type TransactionSimulationResults struct {
ScResults map[string]*transaction.ApiSmartContractResult `json:"scResults,omitempty"`
Receipts map[string]*transaction.ApiReceipt `json:"receipts,omitempty"`
Hash string `json:"hash,omitempty"`
Logs *transaction.ApiLogs `json:"logs,omitempty"`
}

// TransactionSimulationResponseData represents the format of the data field of a transaction simulation response
Expand Down
4 changes: 2 additions & 2 deletions process/scQueryProcessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ func (scQueryProcessor *SCQueryProcessor) ExecuteQuery(query *data.SCQuery) (*vm
return nil, data.BlockInfo{}, err
}

request := scQueryProcessor.createRequestFromQuery(query)

response := data.ResponseVmValue{}
for _, observer := range observers {
request := scQueryProcessor.createRequestFromQuery(query)

params := url.Values{}
if query.BlockNonce.HasValue {
params.Add(blockNonce, fmt.Sprintf("%d", query.BlockNonce.Value))
Expand Down
Loading