Initial web gateway code
This commit is contained in:
130
Gateway.go
130
Gateway.go
@@ -8,18 +8,24 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/PeernetOfficial/core"
|
"github.com/PeernetOfficial/core"
|
||||||
|
"github.com/PeernetOfficial/core/btcec"
|
||||||
|
"github.com/PeernetOfficial/core/webapi"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startWebGateway(backend *core.Backend) {
|
func startWebGateway(backend *core.Backend) {
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.PathPrefix("/").Handler(http.HandlerFunc(webGatewayHandler)).Methods("GET")
|
router.PathPrefix("/").Handler(http.HandlerFunc(webGatewayHandler(backend))).Methods("GET")
|
||||||
router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir(config.WebFiles)))).Methods("GET")
|
|
||||||
|
|
||||||
for _, listen := range config.WebListen {
|
for _, listen := range config.WebListen {
|
||||||
go startWebServer(backend, listen, config.WebUseSSL, config.WebCertificateFile, config.WebCertificateKey, router, "Web Listen", parseDuration(config.WebTimeoutRead), parseDuration(config.WebTimeoutWrite))
|
go startWebServer(backend, listen, config.WebUseSSL, config.WebCertificateFile, config.WebCertificateKey, router, "Web Listen", parseDuration(config.WebTimeoutRead), parseDuration(config.WebTimeoutWrite))
|
||||||
@@ -77,6 +83,122 @@ func webRedirect80(listen80 string) {
|
|||||||
http.ListenAndServe(net.JoinHostPort(listen80, "80"), http.HandlerFunc(redirect))
|
http.ListenAndServe(net.JoinHostPort(listen80, "80"), http.HandlerFunc(redirect))
|
||||||
}
|
}
|
||||||
|
|
||||||
func webGatewayHandler(w http.ResponseWriter, r *http.Request) {
|
func webGatewayHandler(backend *core.Backend) func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Error(w, "404 page not found", http.StatusNotFound)
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// For security and simplicity reasons, below paths are hard-coded.
|
||||||
|
// Using arbitrary user input is an avoidable security risk here.
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/", "/index.html":
|
||||||
|
http.ServeFile(w, r, path.Join(config.WebFiles, "index.html"))
|
||||||
|
return
|
||||||
|
case "/favicon.ico":
|
||||||
|
http.ServeFile(w, r, path.Join(config.WebFiles, "favicon.ico"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume it is in the format "/[blockchain public key]/[file hash]".
|
||||||
|
|
||||||
|
// Remove slash prefix and suffix.
|
||||||
|
pathA := strings.TrimPrefix(strings.TrimSuffix(r.URL.Path, "/"), "/")
|
||||||
|
|
||||||
|
pathParts := strings.Split(pathA, "/")
|
||||||
|
if len(pathParts) != 1 && len(pathParts) != 2 {
|
||||||
|
http.Error(w, "404 not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default timeout for connection is 10 seconds. This will be an optional parameter in the future.
|
||||||
|
timeout := 10 * time.Second
|
||||||
|
|
||||||
|
// First part must be the public key as peer ID or node ID, hex encoded. Form: "/[blockchain public key]"
|
||||||
|
nodeIDA := pathParts[0]
|
||||||
|
nodeID, validNodeID := webapi.DecodeBlake3Hash(nodeIDA)
|
||||||
|
publicKey, errPK := core.PublicKeyFromPeerID(nodeIDA)
|
||||||
|
|
||||||
|
if !validNodeID && errPK != nil {
|
||||||
|
http.Error(w, "404 not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !validNodeID {
|
||||||
|
nodeID = []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a blockchain is requested.
|
||||||
|
// The format must be "/[blockchain public key]". Part 1 = hex encoding, peer ID or node ID.
|
||||||
|
if len(pathParts) == 1 {
|
||||||
|
webGatewayShowBlockchain(backend, w, r, nodeID, publicKey, timeout)
|
||||||
|
} else if len(pathParts) == 2 {
|
||||||
|
// Check if a specific file on a specific blockchain is requested.
|
||||||
|
// The format must be "/[blockchain public key]/[file hash]". Part 2 = hex encoding, blake3 hash.
|
||||||
|
hash, valid := webapi.DecodeBlake3Hash(pathParts[1])
|
||||||
|
if !valid {
|
||||||
|
http.Error(w, "Invalid file hash.", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
webGatewayShowFile(backend, w, r, nodeID, publicKey, hash, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if an arbitrary directory on a specific blockchain is requested.
|
||||||
|
// Directories are identified by name.
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
//http.Error(w, "test handler", http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webGatewayShowBlockchain(backend *core.Backend, w http.ResponseWriter, r *http.Request, nodeID []byte, publicKey *btcec.PublicKey, timeout time.Duration) {
|
||||||
|
var err error
|
||||||
|
var peer *core.PeerInfo
|
||||||
|
if len(nodeID) != 0 {
|
||||||
|
peer, err = webapi.PeerConnectNode(backend, nodeID, timeout)
|
||||||
|
} else {
|
||||||
|
peer, err = webapi.PeerConnectPublicKey(backend, publicKey, timeout)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Could not connect to remote peer. 😢", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// connection established!
|
||||||
|
text := fmt.Sprintf("Peer %s blockchain height %d version %d\nUser Agent: %s\n", hex.EncodeToString(peer.NodeID), peer.BlockchainHeight, peer.BlockchainVersion, peer.UserAgent)
|
||||||
|
http.Error(w, text, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func webGatewayShowFile(backend *core.Backend, w http.ResponseWriter, r *http.Request, nodeID []byte, publicKey *btcec.PublicKey, fileHash []byte, timeout time.Duration) {
|
||||||
|
var err error
|
||||||
|
var peer *core.PeerInfo
|
||||||
|
if len(nodeID) != 0 {
|
||||||
|
peer, err = webapi.PeerConnectNode(backend, nodeID, timeout)
|
||||||
|
} else {
|
||||||
|
peer, err = webapi.PeerConnectPublicKey(backend, publicKey, timeout)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Could not connect to remote peer. 😢", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: Try webapi.serveFileFromWarehouse
|
||||||
|
|
||||||
|
// Todo: Cache.
|
||||||
|
|
||||||
|
offset := 0
|
||||||
|
limit := 0
|
||||||
|
|
||||||
|
// start the reader
|
||||||
|
//reader, fileSize, transferSize, err := webapi.FileStartReader(peer, fileHash, uint64(offset), uint64(limit), r.Context().Done())
|
||||||
|
reader, _, transferSize, err := webapi.FileStartReader(peer, fileHash, uint64(offset), uint64(limit), r.Context().Done())
|
||||||
|
if reader != nil {
|
||||||
|
defer reader.Close()
|
||||||
|
}
|
||||||
|
if err != nil || reader == nil {
|
||||||
|
http.Error(w, "File not found.", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the right headers
|
||||||
|
//webapi.setContentLengthRangeHeader(w, uint64(offset), transferSize, fileSize, ranges)
|
||||||
|
|
||||||
|
// Start sending the data!
|
||||||
|
io.Copy(w, io.LimitReader(reader, int64(transferSize)))
|
||||||
}
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -8,9 +8,11 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/IncSW/geoip2 v0.1.2 // indirect
|
||||||
github.com/akrylysov/pogreb v0.10.1 // indirect
|
github.com/akrylysov/pogreb v0.10.1 // indirect
|
||||||
github.com/enfipy/locker v1.1.0 // indirect
|
github.com/enfipy/locker v1.1.0 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect
|
golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/IncSW/geoip2 v0.1.2 h1:v7iAyDiNZjHES45P1JPM3SMvkw0VNeJtz0XSVxkRwOY=
|
||||||
|
github.com/IncSW/geoip2 v0.1.2/go.mod h1:adcasR40vXiUBjtzdaTTKL/6wSf+fgO4M8Gve/XzPUk=
|
||||||
github.com/PeernetOfficial/core v0.0.0-20220601150942-0e3e8dc9885c h1:n+NGYHQulohXk//v2LYOgCVsbL9h5COupt+2ZhbtGqs=
|
github.com/PeernetOfficial/core v0.0.0-20220601150942-0e3e8dc9885c h1:n+NGYHQulohXk//v2LYOgCVsbL9h5COupt+2ZhbtGqs=
|
||||||
github.com/PeernetOfficial/core v0.0.0-20220601150942-0e3e8dc9885c/go.mod h1:Ek4/3aKyKnA/KDSNe/yeorX5TA0fckpN7etZ+nCelGc=
|
github.com/PeernetOfficial/core v0.0.0-20220601150942-0e3e8dc9885c/go.mod h1:Ek4/3aKyKnA/KDSNe/yeorX5TA0fckpN7etZ+nCelGc=
|
||||||
github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w=
|
github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w=
|
||||||
@@ -8,6 +10,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
|||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
|
|||||||
Reference in New Issue
Block a user