Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
48 changes: 45 additions & 3 deletions cmd/booster-http/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@ package main

import (
"fmt"
"github.com/ipfs/go-libipfs/gateway"
"math/big"
"mime"
"net/http"
"net/url"
"strings"

"github.com/ethereum/go-ethereum/common"
"github.com/ipfs/go-libipfs/gateway"
"github.com/statechannels/go-nitro/rpc"
"github.com/statechannels/go-nitro/types"
)

type gatewayHandler struct {
gwh http.Handler
supportedFormats map[string]struct{}
nitroRpcClient *rpc.RpcClient
}

func newGatewayHandler(gw *BlocksGateway, supportedFormats []string) http.Handler {
func newGatewayHandler(gw *BlocksGateway, supportedFormats []string, nitroRpcClient *rpc.RpcClient) http.Handler {
headers := map[string][]string{}
gateway.AddAccessControlHeaders(headers)

Expand All @@ -22,9 +29,11 @@ func newGatewayHandler(gw *BlocksGateway, supportedFormats []string) http.Handle
fmtsMap[f] = struct{}{}
}

// TODO: For the integration demo, we need to allow CORS requests to the gateway.
return &gatewayHandler{
gwh: gateway.NewHandler(gateway.Config{Headers: headers}, gw),
gwh: &corsHandler{gateway.NewHandler(gateway.Config{Headers: headers}, gw)},
supportedFormats: fmtsMap,
nitroRpcClient: nitroRpcClient,
}
}

Expand All @@ -43,10 +52,43 @@ func (h *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

if h.nitroRpcClient != nil {

params, _ := url.ParseQuery(r.URL.RawQuery)
if !params.Has("channelId") {
webError(w, fmt.Errorf("a valid channel id must be provided"), http.StatusPaymentRequired)
return
}
rawChId := params.Get("channelId")

chId := types.Destination(common.HexToHash(rawChId))
if (chId == types.Destination{} || chId.IsZero()) {
webError(w, fmt.Errorf("a valid channel id must be provided"), http.StatusPaymentRequired)
return
}
// TODO: Allow this to be configurable
expectedPaymentAmount := big.NewInt(10)

hasPaid, err := checkPaymentChannelBalance(h.nitroRpcClient, chId, expectedPaymentAmount)
if err != nil {
webError(w, err, http.StatusPaymentRequired)
return
}

if !hasPaid {
webError(w, fmt.Errorf("payment of %d required", expectedPaymentAmount.Uint64()), http.StatusPaymentRequired)
return
}
}

h.gwh.ServeHTTP(w, r)
}

func webError(w http.ResponseWriter, err error, code int) {
// TODO: This is a hack to allow CORS requests to the gateway for the boost integration demo.
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "*")
fmt.Printf("ERROR CODE %d\n", code)
http.Error(w, err.Error(), code)
}

Expand Down
6 changes: 3 additions & 3 deletions cmd/booster-http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const testFile = "test/test_file"
func TestNewHttpServer(t *testing.T) {
// Create a new mock Http server
ctrl := gomock.NewController(t)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mocks_booster_http.NewMockHttpServerApi(ctrl), nil)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mocks_booster_http.NewMockHttpServerApi(ctrl), nil, nil)
err := httpServer.Start(context.Background())
require.NoError(t, err)
waitServerUp(t, 7777)
Expand All @@ -42,7 +42,7 @@ func TestHttpGzipResponse(t *testing.T) {
// Create a new mock Http server with custom functions
ctrl := gomock.NewController(t)
mockHttpServer := mocks_booster_http.NewMockHttpServerApi(ctrl)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mockHttpServer, nil)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mockHttpServer, nil, nil)
err := httpServer.Start(context.Background())
require.NoError(t, err)
waitServerUp(t, 7777)
Expand Down Expand Up @@ -109,7 +109,7 @@ func TestHttpInfo(t *testing.T) {

// Create a new mock Http server
ctrl := gomock.NewController(t)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mocks_booster_http.NewMockHttpServerApi(ctrl), nil)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mocks_booster_http.NewMockHttpServerApi(ctrl), nil, nil)
err := httpServer.Start(context.Background())
require.NoError(t, err)
waitServerUp(t, 7777)
Expand Down
18 changes: 18 additions & 0 deletions cmd/booster-http/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ var runCmd = &cli.Command{
Usage: "Start a booster-http process",
Before: before,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "nitro-enabled",
Usage: "enables nitro micro payments",
Value: false,
},
&cli.StringFlag{
Name: "nitro-endpoint",
Usage: "the endpoint for the nitro server",
Value: "127.0.0.1:4007",
},

&cli.BoolFlag{
Name: "pprof",
Usage: "run pprof web server on localhost:6070",
Expand Down Expand Up @@ -204,13 +215,20 @@ var runCmd = &cli.Command{
filtered := filters.NewFilteredBlockstore(rbs, multiFilter)
opts.Blockstore = filtered
}

nitroOpts := &NitroOptions{
Enabled: cctx.Bool("nitro-enabled"),
Endpoint: cctx.String("nitro-endpoint"),
}

sapi := serverApi{ctx: ctx, bapi: bapi, sa: sa}
server := NewHttpServer(
cctx.String("base-path"),
cctx.String("address"),
cctx.Int("port"),
sapi,
opts,
nitroOpts,
)

// Start the server
Expand Down
56 changes: 53 additions & 3 deletions cmd/booster-http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import (
"errors"
"fmt"
"io"
"math/big"
"net"
"net/http"
"net/url"
"strings"
"time"

"github.com/NYTimes/gziphandler"
"github.com/ethereum/go-ethereum/common"
"github.com/fatih/color"
"github.com/filecoin-project/boost-gfm/piecestore"
"github.com/filecoin-project/boost-gfm/retrievalmarket"
Expand All @@ -25,6 +28,8 @@ import (
"github.com/ipfs/go-datastore"
blockstore "github.com/ipfs/go-ipfs-blockstore"
offline "github.com/ipfs/go-ipfs-exchange-offline"
nrpc "github.com/statechannels/go-nitro/rpc"
"github.com/statechannels/go-nitro/types"
"go.opencensus.io/stats"
)

Expand Down Expand Up @@ -52,6 +57,8 @@ type HttpServer struct {
ctx context.Context
cancel context.CancelFunc
server *http.Server

nitroRpcClient *nrpc.RpcClient
}

type HttpServerApi interface {
Expand All @@ -66,11 +73,26 @@ type HttpServerOptions struct {
SupportedResponseFormats []string
}

func NewHttpServer(path string, listenAddr string, port int, api HttpServerApi, opts *HttpServerOptions) *HttpServer {
type NitroOptions struct {
Enabled bool
Endpoint string
}

func NewHttpServer(path string, listenAddr string, port int, api HttpServerApi, opts *HttpServerOptions, nitroOpts *NitroOptions) *HttpServer {
if opts == nil {
opts = &HttpServerOptions{ServePieces: true}
}
return &HttpServer{path: path, listenAddr: listenAddr, port: port, api: api, opts: *opts, idxPage: parseTemplate(*opts)}
var rpcClient *nrpc.RpcClient
var err error
if nitroOpts != nil && nitroOpts.Enabled {

rpcClient, err = nrpc.NewHttpRpcClient(nitroOpts.Endpoint)
if err != nil {
panic(err)
}
}
return &HttpServer{path: path, port: port, api: api, opts: *opts, idxPage: parseTemplate(*opts), nitroRpcClient: rpcClient}

}

func (s *HttpServer) pieceBasePath() string {
Expand All @@ -95,7 +117,7 @@ func (s *HttpServer) Start(ctx context.Context) error {
if err != nil {
return fmt.Errorf("creating blocks gateway: %w", err)
}
handler.Handle(s.ipfsBasePath(), newGatewayHandler(gw, s.opts.SupportedResponseFormats))
handler.Handle(s.ipfsBasePath(), newGatewayHandler(gw, s.opts.SupportedResponseFormats, s.nitroRpcClient))
}

handler.HandleFunc("/", s.handleIndex)
Expand Down Expand Up @@ -155,6 +177,7 @@ func (s *HttpServer) handleByPieceCid(w http.ResponseWriter, r *http.Request) {
return
}

// TODO: Update to parse out multiple url params
pieceCidStr := r.URL.Path[prefixLen:]
pieceCid, err := cid.Parse(pieceCidStr)
if err != nil {
Expand All @@ -164,6 +187,33 @@ func (s *HttpServer) handleByPieceCid(w http.ResponseWriter, r *http.Request) {
return
}

if s.nitroRpcClient != nil {
params, _ := url.ParseQuery(r.URL.RawQuery)
if !params.Has("channelId") {
writeError(w, r, http.StatusPaymentRequired, "a valid channel id must be provided")
return
}
rawChId := params.Get("channelId")
chId := types.Destination(common.HexToHash(rawChId))

if (chId == types.Destination{}) {
writeError(w, r, http.StatusPaymentRequired, "a valid channel id must be provided")
return
}
// TODO: Allow this to be configurable
expectedPaymentAmount := big.NewInt(10)

hasPaid, err := checkPaymentChannelBalance(s.nitroRpcClient, chId, expectedPaymentAmount)
if err != nil {
writeError(w, r, http.StatusInternalServerError, err.Error())
return
}

if !hasPaid {
writeError(w, r, http.StatusPaymentRequired, fmt.Sprintf("payment of %d required", expectedPaymentAmount.Uint64()))
return
}
}
// Get a reader over the piece
content, err := s.getPieceContent(ctx, pieceCid)
if err != nil {
Expand Down
35 changes: 34 additions & 1 deletion cmd/booster-http/util.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package main

import "fmt"
import (
"fmt"
"errors"
"math/big"
"net/http"

"github.com/statechannels/go-nitro/rpc"
"github.com/statechannels/go-nitro/types"
)

func addCommas(count uint64) string {
str := fmt.Sprintf("%d", count)
Expand All @@ -9,3 +17,28 @@ func addCommas(count uint64) string {
}
return str
}

// checkPaymentChannelBalance checks a payment channel balance and returns true if the AmountPaid is greater than the expected amount
func checkPaymentChannelBalance(rpcClient *rpc.RpcClient, paymentChannelId types.Destination, expectedAmount *big.Int) (bool, error) {
if rpcClient == nil {
return false, errors.New("the rpcClient is nil")
}
payCh := rpcClient.GetVirtualChannel(paymentChannelId)
return (payCh.Balance.PaidSoFar.ToInt().Cmp(expectedAmount) > 0), nil
}

type corsHandler struct {
sub http.Handler
}

func (h *corsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT")
w.Header().Set("Access-Control-Allow-Headers", "*")
if r.Method == "OPTIONS" {
_, _ = w.Write([]byte("OK"))
return
}

h.sub.ServeHTTP(w, r)
}
Loading