/
ctxhandler.go
191 lines (152 loc) · 6.46 KB
/
ctxhandler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package main
import (
"fmt"
"net/http"
"os"
"sort"
"strings"
"time"
"github.com/vennd/enu/consts"
"github.com/vennd/enu/counterpartyhandlers"
"github.com/vennd/enu/database"
"github.com/vennd/enu/enulib"
"github.com/vennd/enu/generalhandlers"
"github.com/vennd/enu/handlers"
"github.com/vennd/enu/log"
"github.com/vennd/enu/ripplehandlers"
"github.com/vennd/enu/internal/golang.org/x/net/context"
)
/*
type appError struct {
Error error
Message string
Code int
}
*/
type blockchainHandler func(context.Context, http.ResponseWriter, *http.Request, map[string]interface{}) *enulib.AppError
type blockchainFunction map[string]blockchainHandler
// Contains the function to call for each respective blockchain and requestType
var blockchainFunctions = map[string]blockchainFunction{
"counterparty": {
// Address handlers
"address": counterpartyhandlers.AddressCreate,
"walletCreate": counterpartyhandlers.WalletCreate,
"walletPayment": counterpartyhandlers.WalletSend,
"walletBalance": counterpartyhandlers.WalletBalance,
"activateaddress": counterpartyhandlers.ActivateAddress,
// Asset handlers
"asset": counterpartyhandlers.AssetCreate,
"getasset": generalhandlers.GetAsset,
"dividend": counterpartyhandlers.DividendCreate,
"issuances": counterpartyhandlers.AssetIssuances,
"ledger": counterpartyhandlers.AssetLedger,
"getdividend": counterpartyhandlers.GetDividend,
// Payment handlers
"simplepayment": counterpartyhandlers.PaymentCreate,
"paymentretry": counterpartyhandlers.PaymentRetry,
"getpayment": generalhandlers.GetPayment,
"paymentbyaddress": generalhandlers.GetPaymentsByAddress,
},
"ripple": {
// Address handlers
"walletCreate": ripplehandlers.WalletCreate,
"walletPayment": ripplehandlers.WalletSend,
"walletBalance": ripplehandlers.WalletBalance,
"activateaddress": ripplehandlers.ActivateAddress,
// Payment handlers
"getpayment": generalhandlers.GetPayment,
"paymentbyaddress": generalhandlers.GetPaymentsByAddress,
// Asset handlers
"asset": ripplehandlers.AssetCreate,
"getasset": generalhandlers.GetAsset,
// Ripple specific
"getrippleledgerstatus": ripplehandlers.GetRippleLedgerStatus,
// Unsupported
"address": ripplehandlers.Unhandled,
"dividend": ripplehandlers.Unhandled,
},
}
func handle(c context.Context, w http.ResponseWriter, r *http.Request) *enulib.AppError {
// check generic args and parse
c2, m, err := handlers.CheckAndParseJsonCTX(c, w, r)
if err != nil {
// Status errors are handled inside CheckAndParseJsonCTX, so we just exit gracefully
return nil
}
// Look up the required function in the map and execute
blockchainId := c2.Value(consts.BlockchainIdKey).(string)
requestType := c2.Value(consts.RequestTypeKey).(string)
log.FluentfContext(consts.LOGINFO, c, "Handling blockchainId: %s, requestType: %s", blockchainId, requestType)
// If the specified handler can't be found, return a 404
if blockchainFunctions[blockchainId][requestType] == nil {
log.FluentfContext(consts.LOGINFO, c, "No function could be found in blockchainFunctions to handle blockchainId: %s, requestType: %s", blockchainId, requestType)
handlers.ReturnNotFoundWithCustomError(c, w, consts.GenericErrors.FunctionNotAvailable.Code, consts.GenericErrors.FunctionNotAvailable.Description)
return nil
}
blockchainFunctions[blockchainId][requestType](c2, w, r, m)
return nil
}
type ctxHandler func(context.Context, http.ResponseWriter, *http.Request) *enulib.AppError
func (fn ctxHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Generate an requestId
requestId := enulib.GenerateRequestId()
// Create a context with only the requestId in it initially
parent := context.TODO()
ctx := context.WithValue(parent, consts.RequestIdKey, requestId)
// Get the env we are running in
env := os.Getenv("ENV")
if env == "" {
env = "dev"
}
ctx = context.WithValue(ctx, consts.EnvKey, env)
log.FluentfContext(consts.LOGINFO, ctx, "%s %s entered.", r.Method, r.URL.Path)
accessKey, err := handlers.CheckHeaderGeneric(ctx, w, r)
if err != nil {
return
}
// Add to the context the accessKey now that we know it
ctx1 := context.WithValue(ctx, consts.AccessKeyKey, accessKey)
// Determine blockchain this request is acting upon
usersDefaultBlockchain := database.GetBlockchainIdByUserKey(accessKey)
p := strings.Split(r.URL.Path, "/")
requestBlockchainId := p[1]
supportedBlockchains := consts.SupportedBlockchains
sort.Strings(supportedBlockchains)
// Search if the first part of the path after the "/" is the blockchain name. ie "/counterparty" or "/ripple"
i := sort.SearchStrings(supportedBlockchains, requestBlockchainId)
blockchainValid := i < len(supportedBlockchains) && supportedBlockchains[i] == requestBlockchainId
// Search if the user has a valid default associated with their access key
i = sort.SearchStrings(supportedBlockchains, usersDefaultBlockchain)
userBlockchainIdValid := i < len(supportedBlockchains) && supportedBlockchains[i] == usersDefaultBlockchain
// If the blockchain specified in the path isn't valid and a default blockchainId isn't set in the userkey then fail
if blockchainValid == false && userBlockchainIdValid == false {
log.FluentfContext(consts.LOGINFO, ctx, "Unsupported blockchain. Valid values: %s", strings.Join(supportedBlockchains, ", "))
e := fmt.Sprintf("Unsupported blockchain. Valid values: %s", strings.Join(supportedBlockchains, ", "))
handlers.ReturnServerErrorWithCustomError(ctx, w, consts.GenericErrors.UnsupportedBlockchain.Code, e)
return
}
// If the blockchain was explicitly given in the resource path, use it. Otherwise use the blockchain from the user's profile
var blockchainId string
if blockchainValid == true {
log.FluentfContext(consts.LOGINFO, ctx, "Using blockchain specifed in URL: %s", requestBlockchainId)
blockchainId = requestBlockchainId
} else {
log.FluentfContext(consts.LOGINFO, ctx, "Using user's default blockchain: %s", usersDefaultBlockchain)
blockchainId = usersDefaultBlockchain
}
// Add blockchain to the context now that we know it
ctx = context.WithValue(ctx1, consts.BlockchainIdKey, blockchainId)
log.FluentfContext(consts.LOGINFO, ctx, "Calling context function.")
// run function
if e := fn(ctx, w, r); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
// Log how long it took
log.FluentfContext(consts.LOGINFO, ctx,
"%s,%s,%s",
r.Method,
r.RequestURI,
time.Since(start),
)
}