diff --git a/Cmd b/Cmd index 3458b86..b015649 100755 Binary files a/Cmd and b/Cmd differ diff --git a/Gateway.go b/Gateway.go index 678bda1..b705b90 100644 --- a/Gateway.go +++ b/Gateway.go @@ -7,198 +7,208 @@ Author: Peter Kleissner package main import ( - "crypto/tls" - "encoding/hex" - "fmt" - "io" - "net" - "net/http" - "path" - "strings" - "time" + "crypto/tls" + "encoding/hex" + "fmt" + "io" + "net" + "net/http" + "path" + "strings" + "time" - "github.com/PeernetOfficial/core" - "github.com/PeernetOfficial/core/btcec" - "github.com/PeernetOfficial/core/webapi" - "github.com/gorilla/mux" + "github.com/PeernetOfficial/core" + "github.com/PeernetOfficial/core/btcec" + "github.com/PeernetOfficial/core/webapi" + "github.com/gorilla/mux" ) func startWebGateway(backend *core.Backend) { - router := mux.NewRouter() - router.PathPrefix("/").Handler(http.HandlerFunc(webGatewayHandler(backend))).Methods("GET") + router := mux.NewRouter() + router.PathPrefix("/").Handler(http.HandlerFunc(webGatewayHandler(backend))).Methods("GET") - for _, listen := range config.WebListen { - go startWebServer(backend, listen, config.WebUseSSL, config.WebCertificateFile, config.WebCertificateKey, router, "Web Listen", parseDuration(config.WebTimeoutRead), parseDuration(config.WebTimeoutWrite)) - } + for _, listen := range config.WebListen { + go startWebServer(backend, listen, config.WebUseSSL, config.WebCertificateFile, config.WebCertificateKey, router, "Web Listen", parseDuration(config.WebTimeoutRead), parseDuration(config.WebTimeoutWrite)) + } - if config.Redirect80 != "" { - go webRedirect80(config.Redirect80) - } + if config.Redirect80 != "" { + go webRedirect80(config.Redirect80) + } - // wait forever - select {} + // wait forever + select {} } // startWebServer starts the web-server and may block forever func startWebServer(backend *core.Backend, WebListen string, UseSSL bool, CertificateFile, CertificateKey string, Handler http.Handler, Info string, ReadTimeout, WriteTimeout time.Duration) { - //func startWebServer(backend *core.Backend, webListen string, useSSL bool, certificateFile, certificateKey string, server *http.Server) { - backend.LogError("startWebServer", "Web Gateway to listen on '%s'", WebListen) + //func startWebServer(backend *core.Backend, webListen string, useSSL bool, certificateFile, certificateKey string, server *http.Server) { + backend.LogError("startWebServer", "Web Gateway to listen on '%s'", WebListen) - tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} // for security reasons disable TLS 1.0/1.1 + tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} // for security reasons disable TLS 1.0/1.1 - server := &http.Server{ - Addr: WebListen, - Handler: Handler, - ReadTimeout: ReadTimeout, // ReadTimeout is the maximum duration for reading the entire request, including the body. - WriteTimeout: WriteTimeout, // WriteTimeout is the maximum duration before timing out writes of the response. This includes processing time and is therefore the max time any HTTP function may take. - //IdleTimeout: IdleTimeout, // IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled. - TLSConfig: tlsConfig, - } + server := &http.Server{ + Addr: WebListen, + Handler: Handler, + ReadTimeout: ReadTimeout, // ReadTimeout is the maximum duration for reading the entire request, including the body. + WriteTimeout: WriteTimeout, // WriteTimeout is the maximum duration before timing out writes of the response. This includes processing time and is therefore the max time any HTTP function may take. + //IdleTimeout: IdleTimeout, // IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled. + TLSConfig: tlsConfig, + } - if UseSSL { - // HTTPS - if err := server.ListenAndServeTLS(CertificateFile, CertificateKey); err != nil { - backend.LogError("startWebServer", "Error listening on '%s': %v\n", WebListen, err) - } - } else { - // HTTP - if err := server.ListenAndServe(); err != nil { - backend.LogError("startWebServer", "Error listening on '%s': %v\n", WebListen, err) - } - } + if UseSSL { + // HTTPS + if err := server.ListenAndServeTLS(CertificateFile, CertificateKey); err != nil { + backend.LogError("startWebServer", "Error listening on '%s': %v\n", WebListen, err) + } + } else { + // HTTP + if err := server.ListenAndServe(); err != nil { + backend.LogError("startWebServer", "Error listening on '%s': %v\n", WebListen, err) + } + } } // parseDuration is the same as time.ParseDuration without returning an error. Valid units are ms, s, m, h. For example "10s". func parseDuration(input string) (result time.Duration) { - result, _ = time.ParseDuration(input) - return + result, _ = time.ParseDuration(input) + return } func redirect(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently) + http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently) } func webRedirect80(listen80 string) { - // redirect HTTP -> HTTPS - http.ListenAndServe(net.JoinHostPort(listen80, "80"), http.HandlerFunc(redirect)) + // redirect HTTP -> HTTPS + http.ListenAndServe(net.JoinHostPort(listen80, "80"), http.HandlerFunc(redirect)) } func webGatewayHandler(backend *core.Backend) func(w http.ResponseWriter, r *http.Request) { - 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 - } + 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]". + // Assume it is in the format "/[blockchain public key]/[file hash]". - // Remove slash prefix and suffix. - pathA := strings.TrimPrefix(strings.TrimSuffix(r.URL.Path, "/"), "/") + // 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 - } + 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 + // 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) + // 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{} - } + 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 - } + // 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) - } + 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 + // Check if an arbitrary directory on a specific blockchain is requested. + // Directories are identified by name. + // TODO - //http.Error(w, "test handler", http.StatusOK) - } + //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 - } + 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) + // 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 - } + 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: Try webapi.serveFileFromWarehouse - // Todo: Cache. + // Todo: Cache. - offset := 0 - limit := 0 + 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 - } + // 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) + // if get request ?download=true + // trigger download of the file + download := r.URL.Query().Get("download") + if download == "true" { + io.Copy(w, io.LimitReader(reader, int64(transferSize))) + return + } else { - // Start sending the data! - io.Copy(w, io.LimitReader(reader, int64(transferSize))) + } + + // set the right headers + //webapi.setContentLengthRangeHeader(w, uint64(offset), transferSize, fileSize, ranges) + + // Start sending the data! + io.Copy(w, io.LimitReader(reader, int64(transferSize))) } diff --git a/html/download.html b/html/download.html index ff66550..809c6b8 100644 --- a/html/download.html +++ b/html/download.html @@ -10,7 +10,7 @@ - + @@ -78,10 +78,10 @@ - - - - + + + +