Skip to content

Commit

Permalink
Insight API [6], [6.1], [15], [15.1], [16], [16.1], [16.2], [16.3], […
Browse files Browse the repository at this point in the history
…16.4] (decred#493)

Insight endpoints listed in decred#400: [6], [6.1], [15], [15.1], [16], [16.1], [16.2], [16.3], [16.4]

* CtxAddress, ValidatePost middleware, check content type, Remove unecessary log entries, Compress output
  • Loading branch information
papacarp authored and chappjc committed Jun 8, 2018
1 parent fc0161f commit 031d7ee
Show file tree
Hide file tree
Showing 11 changed files with 641 additions and 48 deletions.
149 changes: 149 additions & 0 deletions api/insight/apimiddleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"strconv"

apitypes "github.com/decred/dcrdata/api/types"
m "github.com/decred/dcrdata/middleware"
Expand All @@ -19,6 +20,11 @@ type contextKey int

const (
ctxRawHexTx contextKey = iota
ctxFrom
ctxTo
ctxNoAsm
ctxNoScriptSig
ctxNoSpent
)

// BlockHashPathAndIndexCtx is a middleware that embeds the value at the url
Expand Down Expand Up @@ -91,3 +97,146 @@ func (c *insightApiContext) PostBroadcastTxCtx(next http.Handler) http.Handler {
next.ServeHTTP(w, r.WithContext(ctx))
})
}

// GetToCtx retrieves the ctxTo data ("to") from the request context. If not
// set, the return value ok is false.
func (c *insightApiContext) GetToCtx(r *http.Request) (int64, bool) {
to, ok := r.Context().Value(ctxTo).(int)
if !ok {
return int64(0), false
}
return int64(to), true
}

// GetFromCtx retrieves the ctxFrom data ("from") from the request context.
// If not set, the return value is 0
func (c *insightApiContext) GetFromCtx(r *http.Request) int64 {
from, ok := r.Context().Value(ctxFrom).(int)
if !ok {
return int64(0)
}
return int64(from)
}

// GetNoAsmCtx retrieves the ctxNoAsm data ("noAsm") from the request context.
// If not set, the return value is false.
func (c *insightApiContext) GetNoAsmCtx(r *http.Request) bool {
noAsm, ok := r.Context().Value(ctxNoAsm).(bool)
if !ok {
return false
}
return noAsm
}

// GetNoScriptSigCtx retrieves the ctxNoScriptSig data ("noScriptSig") from the
// request context. If not set, the return value is false.
func (c *insightApiContext) GetNoScriptSigCtx(r *http.Request) bool {
noScriptSig, ok := r.Context().Value(ctxNoScriptSig).(bool)
if !ok {
return false
}
return noScriptSig
}

// GetNoSpentCtx retrieves the ctxNoSpent data ("noSpent") from the
// request context. If not set, the return value is false.
func (c *insightApiContext) GetNoSpentCtx(r *http.Request) bool {
noSpent, ok := r.Context().Value(ctxNoSpent).(bool)
if !ok {
return false
}
return noSpent
}

// FromToPaginationCtx will parse the query parameters for from/to values.
func (c *insightApiContext) FromToPaginationCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
to, from := r.FormValue("to"), r.FormValue("from")
fromint, err := strconv.Atoi(from)
if err == nil {
ctx = context.WithValue(r.Context(), ctxFrom, fromint)
}
toint, err := strconv.Atoi(to)
if err == nil {
ctx = context.WithValue(ctx, ctxTo, toint)
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}

// ValidatePostCtx will confirm Post content length is valid.
func (c *insightApiContext) ValidatePostCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
contentLengthString := r.Header.Get("Content-Length")
contentLength, err := strconv.Atoi(contentLengthString)
if err != nil {
writeInsightError(w, "Content-Length Header must be set")
return
}
// Broadcast Tx has the largest possible body. Cap max content length
// to c.params.MaxTxSize * 2 plus some arbitrary extra for JSON
// encapsulation.
maxPayload := (c.params.MaxTxSize * 2) + 50
if contentLength > maxPayload {
writeInsightError(w, fmt.Sprintf("Maximum Content-Length is %d", maxPayload))
return
}
next.ServeHTTP(w, r)
})
}

// Process params given in post body for an addrs endpoint
func (c *insightApiContext) PostAddrsTxsCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var req apitypes.InsightMultiAddrsTx
var from, to, noAsm, noScriptSig, noSpent int64

body, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
writeInsightError(w, fmt.Sprintf("error reading JSON message: %v", err))
return
}
err = json.Unmarshal(body, &req)
if err != nil {
writeInsightError(w, fmt.Sprintf("Failed to parse request: %v", err))
return
}
// Successful extraction of Body JSON
ctx := context.WithValue(r.Context(), m.CtxAddress, req.Addresses)

if req.From != "" {
from, err = req.From.Int64()
if err == nil {
ctx = context.WithValue(ctx, ctxFrom, int(from))
}
}
if req.To != "" {
to, err = req.To.Int64()
if err == nil {
ctx = context.WithValue(ctx, ctxTo, int(to))
}
}
if req.NoAsm != "" {
noAsm, err = req.NoAsm.Int64()
if err == nil && noAsm != 0 {
ctx = context.WithValue(ctx, ctxNoAsm, true)
}
}
if req.NoScriptSig != "" {
noScriptSig, err = req.To.Int64()
if err == nil && noScriptSig != 0 {
ctx = context.WithValue(ctx, ctxNoScriptSig, true)
}
}

if req.NoSpent != "" {
noSpent, err = req.NoSpent.Int64()
if err == nil && noSpent != 0 {
ctx = context.WithValue(ctx, ctxNoSpent, true)
}
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}
12 changes: 8 additions & 4 deletions api/insight/apirouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func NewInsightApiRouter(app *insightApiContext, userRealIP bool) ApiMux {

mux.Use(middleware.Logger)
mux.Use(middleware.Recoverer)
mux.Use(middleware.StripSlashes)
mux.Use(middleware.DefaultCompress)

// Block endpoints
mux.With(m.BlockDateQueryCtx).Get("/blocks", app.getBlockSummaryByTime)
Expand All @@ -48,7 +50,8 @@ func NewInsightApiRouter(app *insightApiContext, userRealIP bool) ApiMux {
mux.With(m.BlockIndexOrHashPathCtx).Get("/rawblock/{idx}", app.getRawBlock)

// Transaction endpoints
mux.With(app.PostBroadcastTxCtx).Post("/tx/send", app.broadcastTransactionRaw)
mux.With(middleware.AllowContentType("application/json"),
app.ValidatePostCtx, app.PostBroadcastTxCtx).Post("/tx/send", app.broadcastTransactionRaw)
mux.With(m.TransactionHashCtx).Get("/tx/{txid}", app.getTransaction)
mux.With(m.TransactionHashCtx).Get("/rawtx/{txid}", app.getTransactionHex)
mux.With(m.TransactionsCtx).Get("/txs", app.getTransactions)
Expand All @@ -58,13 +61,14 @@ func NewInsightApiRouter(app *insightApiContext, userRealIP bool) ApiMux {

// Addresses endpoints
mux.Route("/addrs", func(rd chi.Router) {
mux.Route("/{address}", func(ra chi.Router) {
ra.Use(m.AddressPathCtx, m.PaginationCtx)
rd.Route("/{address}", func(ra chi.Router) {
ra.Use(m.AddressPathCtx, app.FromToPaginationCtx)
ra.Get("/txs", app.getAddressesTxn)
ra.Get("/utxo", app.getAddressesTxnOutput)
})
// POST methods
rd.With(m.PaginationCtx, m.AddressPostCtx).Post("/txs", app.getAddressesTxn)
rd.With(middleware.AllowContentType("application/json"),
app.ValidatePostCtx, app.PostAddrsTxsCtx).Post("/txs", app.getAddressesTxn)
rd.With(m.AddressPostCtx).Post("/utxo", app.getAddressesTxnOutput)
})

Expand Down
Loading

0 comments on commit 031d7ee

Please sign in to comment.