Initial web gateway code

This commit is contained in:
Kleissner
2022-09-25 18:34:00 +02:00
parent f8f2d2cbfb
commit ea83cc014d
3 changed files with 132 additions and 4 deletions

View File

@@ -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
View File

@@ -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
View File

@@ -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=