You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: main.go
    path of file: ./repos/lotus/cmd/lotus-fountain
    
    the code of the file until where you have to start completion: package main

import (
	"context"
	"fmt"
	"html/template"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	rice "github.com/GeertJohan/go.rice"
	logging "github.com/ipfs/go-log/v2"
	"github.com/urfave/cli/v2"
	"golang.org/x/xerrors"

	"github.com/filecoin-project/go-address"
	verifregtypes9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg"

	"github.com/filecoin-project/lotus/api/v0api"
	"github.com/filecoin-project/lotus/build"
	"github.com/filecoin-project/lotus/chain/actors"
	"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"
	"github.com/filecoin-project/lotus/chain/types"
	"github.com/filecoin-project/lotus/chain/types/ethtypes"
	lcli "github.com/filecoin-project/lotus/cli"
)

var log = logging.Logger("main")

func main() {
	logging.SetLogLevel("*", "INFO")

	log.Info("Starting fountain")

	local := []*cli.Command{
		runCmd,
	}

	app := &cli.App{
		Name:    "lotus-fountain",
		Usage:   "Devnet token distribution utility",
		Version: build.UserVersion(),
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "repo",
				EnvVars: []string{"LOTUS_PATH"},
				Value:   "~/.lotus", // TODO: Consider XDG_DATA_HOME
			},
		},

		Commands: local,
	}

	if err := app.Run(os.Args); err != nil {
		log.Warn(err)
		return
	}
}

var runCmd = &cli.Command{
	Name:  "run",
	Usage: "Start lotus fountain",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:  "front",
			Value: "127.0.0.1:7777",
		},
		&cli.StringFlag{
			Name: "from",
		},
		&cli.StringFlag{
			Name:    "amount",
			EnvVars: []string{"LOTUS_FOUNTAIN_AMOUNT"},
			Value:   "50",
		},
		&cli.Uint64Flag{
			Name:    "data-cap",
			EnvVars: []string{"LOTUS_DATACAP_AMOUNT"},
			Value:   verifregtypes9.MinVerifiedDealSize.Uint64(),
		},
		&cli.Float64Flag{
			Name:  "captcha-threshold",
			Value: 0.5,
		},
		&cli.StringFlag{
			Name:  "http-server-timeout",
			Value: "30s",
		},
	},
	Action: func(cctx *cli.Context) error {
		sendPerRequest, err := types.ParseFIL(cctx.String("amount"))
		if err != nil {
			return err
		}

		nodeApi, closer, err := lcli.GetFullNodeAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := lcli.ReqContext(cctx)

		v, err := nodeApi.Version(ctx)
		if err != nil {
			return err
		}

		log.Infof("Remote version: %s", v.Version)

		from, err := address.NewFromString(cctx.String("from"))
		if err != nil {
			return xerrors.Errorf("parsing source address (provide correct --from flag!): %w", err)
		}

		h := &handler{
			ctx:            ctx,
			api:            nodeApi,
			from:           from,
			al
    
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "only POST is allowed", http.StatusBadRequest)
		return
	}

	reqIP := r.Header.Get("X-Real-IP")
	if reqIP == "" {
		h, _, err := net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			log.Errorf("could not get ip from: %s, err: %s", r.RemoteAddr, err)
		}
		reqIP = h
	}

	capResp, err := VerifyToken(r.FormValue("g-recaptcha-response"), reqIP)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	if !capResp.Success || capResp.Score < h.recapThreshold {
		log.Infow("spam", "capResp", capResp)
		http.Error(w, "spam protection", http.StatusUnprocessableEntity)
		return
	}

	addressInput := r.FormValue("address")

	var filecoinAddress address.Address
	var decodeError error

	if strings.HasPrefix(addressInput, "0x") {
		ethAddress, err := ethtypes.ParseEthAddress(addressInput)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		filecoinAddress, decodeError = ethAddress.ToFilecoinAddress()
	} else {
		filecoinAddress, decodeError = address.NewFromString(addressInput)
	}

	if decodeError != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if filecoinAddress == address.Undef {
		http.Error(w, "empty address", http.StatusBadRequest)
		return
	}

	// Limit based on wallet address
	limiter := h.limiter.GetWalletLimiter(filecoinAddress.String())
	if !limiter.Allow() {
		http.Error(w, http.StatusText(http.StatusTooManyRequests)+": wallet limit", http.StatusTooManyRequests)
		return
	}

	// Limit based on IP
	if i := net.ParseIP(reqIP); i != nil && i.IsLoopback() {
		log.Errorf("rate limiting localhost: %s", reqIP)
	}

	limiter = h.limiter.GetIPLimiter(reqIP)
	if !limiter.Allow() {
		http.Error(w, http.StatusText(http.StatusTooManyRequests)+": IP limit", http.StatusTooManyRequests)
		return
	}

	// General limiter to allow throttling all messages that can make it into the mpool
	if !h.limiter.Allow() {
		http.Error(w, http.StatusText(http.StatusTooManyRequests)+": global limit", http.StatusTooManyRequests)
		return
	}

	var smsg *types.SignedMessage
	if r.RequestURI == "/send" {
		smsg, err = h.api.MpoolPushMessage(
			h.ctx, &types.Message{
				Value: types.BigInt(h.sendPerRequest),
				From:  h.from,
				To:    filecoinAddress,
			}, nil)
	} else if r.RequestURI == "/datacap" {
		var params []byte
		params, err = actors.SerializeParams(
			&verifregtypes9.AddVerifiedClientParams{
				Address:   filecoinAddress,
				Allowance: h.allowance,
			})
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		smsg, err = h.api.MpoolPushMessage(
			h.ctx, &types.Message{
				Params: params,
				From:   h.from,
				To:     verifreg.Address,
				Method: verifreg.Methods.AddVerifiedClient,
			}, nil)
	} else {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	_, _ = w.Write([]byte(smsg.Cid().String()))
}