1
1
package faucet
2
2
3
3
import (
4
+ "context"
5
+ "encoding/json"
4
6
"errors"
5
7
"fmt"
6
8
"io"
7
9
"net/http"
8
10
"regexp"
9
11
10
12
"github.com/gnolang/faucet/spec"
11
- "github.com/gnolang/faucet/writer"
12
- httpWriter "github.com/gnolang/faucet/writer/http"
13
13
"github.com/gnolang/gno/tm2/pkg/crypto"
14
14
"github.com/gnolang/gno/tm2/pkg/std"
15
15
)
16
16
17
- const (
18
- unableToHandleRequest = "unable to handle faucet request"
19
- faucetSuccess = "successfully executed faucet transfer"
20
- )
17
+ const faucetSuccess = "successfully executed faucet transfer"
21
18
22
19
const DefaultDripMethod = "drip" // the default JSON-RPC method for a faucet drip
23
20
@@ -27,81 +24,106 @@ var (
27
24
errInvalidMethod = errors .New ("unknown RPC method call" )
28
25
)
29
26
30
- // drip is a single Faucet transfer request
31
- type drip struct {
32
- amount std.Coins
33
- to crypto.Address
34
- }
27
+ // wrapJSONRPC wraps the given handler and middlewares into a JSON-RPC 2.0 pipeline
28
+ func wrapJSONRPC (handlerFn HandlerFunc , mws ... Middleware ) http.HandlerFunc {
29
+ callChain := chainMiddlewares (mws ... )(handlerFn )
35
30
36
- var amountRegex = regexp .MustCompile (`^\d+ugnot$` )
31
+ return func (w http.ResponseWriter , r * http.Request ) {
32
+ // Grab the request(s)
33
+ requests , err := parseRequests (r .Body )
34
+ if err != nil {
35
+ http .Error (
36
+ w ,
37
+ fmt .Sprintf ("unable to read request: %s" , err .Error ()),
38
+ http .StatusBadRequest ,
39
+ )
37
40
38
- // defaultHTTPHandler is the default faucet transfer handler
39
- func (f * Faucet ) defaultHTTPHandler (w http.ResponseWriter , r * http.Request ) {
40
- // Load the requests
41
- requestBody , readErr := io .ReadAll (r .Body )
42
- if readErr != nil {
43
- http .Error (
44
- w ,
45
- "unable to read request" ,
46
- http .StatusBadRequest ,
47
- )
41
+ return
42
+ }
48
43
49
- return
50
- }
44
+ var (
45
+ ctx = r . Context ()
51
46
52
- // Extract the requests
53
- requests , err := spec .ExtractBaseRequests (requestBody )
54
- if err != nil {
55
- http .Error (
56
- w ,
57
- "invalid request body" ,
58
- http .StatusBadRequest ,
47
+ responses = make (spec.BaseJSONResponses , 0 )
59
48
)
60
49
61
- return
62
- }
50
+ for _ , req := range requests {
51
+ // Make sure it's a valid base request
52
+ if ! spec .IsValidBaseRequest (req ) {
53
+ responses = append (responses , spec .NewJSONResponse (
54
+ req .ID ,
55
+ nil ,
56
+ spec .NewJSONError ("invalid JSON-RPC 2.0 request" , spec .InvalidRequestErrorCode ),
57
+ ))
58
+
59
+ continue
60
+ }
61
+
62
+ // Parse the request.
63
+ // This executes all the middlewares, and
64
+ // finally the base handler for the endpoint
65
+ resp := callChain (ctx , req )
66
+
67
+ responses = append (responses , resp )
68
+ }
69
+
70
+ w .Header ().Set ("Content-Type" , JSONMimeType )
71
+
72
+ // Create the encoder
73
+ enc := json .NewEncoder (w )
74
+
75
+ if len (responses ) == 1 {
76
+ // Write the JSON response as a single response
77
+ _ = enc .Encode (responses [0 ]) //nolint:errcheck // Fine to leave unchecked
63
78
64
- // Handle the requests
65
- w . Header (). Set ( "Content-Type" , jsonMimeType )
66
- f . handleRequest (
67
- httpWriter . New ( f . logger , w ),
68
- requests ,
69
- )
79
+ return
80
+ }
81
+
82
+ // Write the JSON response as a batch
83
+ _ = enc . Encode ( responses ) //nolint:errcheck // Fine to leave unchecked
84
+ }
70
85
}
71
86
72
- // handleRequest is the common default faucet handler
73
- func ( f * Faucet ) handleRequest ( writer writer. ResponseWriter , requests spec. BaseJSONRequests ) {
74
- // Parse all JSON-RPC requests
75
- responses := make (spec. BaseJSONResponses , len ( requests ))
87
+ // chainMiddlewares combines the given middlewares
88
+ func chainMiddlewares ( mw ... Middleware ) Middleware {
89
+ return func ( final HandlerFunc ) HandlerFunc {
90
+ h := final
76
91
77
- for i , req := range requests {
78
- f .logger .Debug ("incoming request" , "request" , req )
92
+ for i := len (mw ) - 1 ; i >= 0 ; i -- {
93
+ h = mw [i ](h )
94
+ }
79
95
80
- responses [ i ] = f . handleSingleRequest ( req )
96
+ return h
81
97
}
98
+ }
82
99
83
- if len (responses ) == 1 {
84
- // Write the JSON response as a single response
85
- writer .WriteResponse (responses [0 ])
100
+ // parseRequests parses the JSON-RPC requests from the request body
101
+ func parseRequests (body io.Reader ) (spec.BaseJSONRequests , error ) {
102
+ // Load the requests
103
+ requestBody , readErr := io .ReadAll (body )
104
+ if readErr != nil {
105
+ return nil , fmt .Errorf ("unable to read request: %w" , readErr )
106
+ }
86
107
87
- return
108
+ // Extract the requests
109
+ requests , err := spec .ExtractBaseRequests (requestBody )
110
+ if err != nil {
111
+ return nil , fmt .Errorf ("invalid request body: %w" , err )
88
112
}
89
113
90
- // Write the JSON response as a batch
91
- writer .WriteResponse (responses )
114
+ return requests , nil
92
115
}
93
116
94
- // handleSingleRequest validates and executes one drip request
95
- func (f * Faucet ) handleSingleRequest (req * spec.BaseJSONRequest ) * spec.BaseJSONResponse {
96
- // Make sure it's a valid base request
97
- if ! spec .IsValidBaseRequest (req ) {
98
- return spec .NewJSONResponse (
99
- req .ID ,
100
- nil ,
101
- spec .NewJSONError ("invalid JSON-RPC 2.0 request" , spec .InvalidRequestErrorCode ),
102
- )
103
- }
117
+ // drip is a single Faucet transfer request
118
+ type drip struct {
119
+ amount std.Coins
120
+ to crypto.Address
121
+ }
104
122
123
+ var amountRegex = regexp .MustCompile (`^\d+ugnot$` )
124
+
125
+ // defaultHTTPHandler is the default faucet transfer handler
126
+ func (f * Faucet ) defaultHTTPHandler (_ context.Context , req * spec.BaseJSONRequest ) * spec.BaseJSONResponse {
105
127
// Make sure the method call on "/" is "drip"
106
128
if req .Method != DefaultDripMethod {
107
129
return spec .NewJSONResponse (
0 commit comments