Get preferred network IP and refactor server package to increase reusability (#2626)
* Added function to get preffered network IP Also done some refactor work oon server package to make a lot more reusable * Added server.Option and simplified handler funcs * Added serial number deterministically generated from pk * Debugging TLS server connection * Implemented configurable server ip When accessing over the network the server needs to listen on the network port and not localhost or 127.0.0.1 . Also the cert can now have a dedicated IP * Refactor of URL funcs to use the url package * Removed redundant Options pattern in favour of config param * Added full server test using GetOutboundIP * Remove references and usage of Server.port The application does not need to set the port, we rely on the net.Listener to pick a port. * Version bump * Added ToECDSA func and improved cert testing * Added error check in test * Split Server types, embedding raw Server funcs into specialised server types * localhost * Implemented DNS and IP based cert gen ios doesn't allow for restricted ip addresses to be used in a valid tls cert * Replace listener handling with original port store Also added handlers as a parameter of the Server
This commit is contained in:
parent
efa14805bd
commit
7f149f93c1
|
@ -82,7 +82,7 @@ type StatusNode struct {
|
|||
rpcClient *rpc.Client // reference to an RPC client
|
||||
|
||||
downloader *ipfs.Downloader
|
||||
httpServer *server.Server
|
||||
httpServer *server.MediaServer
|
||||
|
||||
discovery discovery.Discovery
|
||||
register *peers.Register
|
||||
|
@ -152,7 +152,7 @@ func (n *StatusNode) GethNode() *node.Node {
|
|||
return n.gethNode
|
||||
}
|
||||
|
||||
func (n *StatusNode) HTTPServer() *server.Server {
|
||||
func (n *StatusNode) HTTPServer() *server.MediaServer {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
|
@ -238,7 +238,7 @@ func (n *StatusNode) startWithDB(config *params.NodeConfig, accs *accounts.Manag
|
|||
|
||||
n.downloader = ipfs.NewDownloader(config.RootDataDir)
|
||||
|
||||
httpServer, err := server.NewServer(n.appDB, n.downloader)
|
||||
httpServer, err := server.NewMediaServer(n.appDB, n.downloader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/images"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/server"
|
||||
)
|
||||
|
||||
// QuotedMessage contains the original text of the message replied to
|
||||
|
@ -35,10 +36,6 @@ type QuotedMessage struct {
|
|||
CommunityID string `json:"communityId,omitempty"`
|
||||
}
|
||||
|
||||
func (m *QuotedMessage) PrepareImageURL(port int) {
|
||||
m.ImageLocalURL = fmt.Sprintf("https://localhost:%d/messages/images?messageId=%s", port, m.ID)
|
||||
}
|
||||
|
||||
type CommandState int
|
||||
|
||||
const (
|
||||
|
@ -177,20 +174,20 @@ type Message struct {
|
|||
ContactRequestState ContactRequestState `json:"contactRequestState,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Message) PrepareServerURLs(port int) {
|
||||
m.Identicon = fmt.Sprintf("https://localhost:%d/messages/identicons?publicKey=%s", port, m.From)
|
||||
func (m *Message) PrepareServerURLs(s *server.MediaServer) {
|
||||
m.Identicon = s.MakeIdenticonURL(m.From)
|
||||
|
||||
if m.QuotedMessage != nil && m.QuotedMessage.ContentType == int64(protobuf.ChatMessage_IMAGE) {
|
||||
m.QuotedMessage.PrepareImageURL(port)
|
||||
m.QuotedMessage.ImageLocalURL = s.MakeImageURL(m.QuotedMessage.ID)
|
||||
}
|
||||
if m.ContentType == protobuf.ChatMessage_IMAGE {
|
||||
m.ImageLocalURL = fmt.Sprintf("https://localhost:%d/messages/images?messageId=%s", port, m.ID)
|
||||
m.ImageLocalURL = s.MakeImageURL(m.ID)
|
||||
}
|
||||
if m.ContentType == protobuf.ChatMessage_AUDIO {
|
||||
m.AudioLocalURL = fmt.Sprintf("https://localhost:%d/messages/audio?messageId=%s", port, m.ID)
|
||||
m.AudioLocalURL = s.MakeAudioURL(m.ID)
|
||||
}
|
||||
if m.ContentType == protobuf.ChatMessage_STICKER {
|
||||
m.StickerLocalURL = fmt.Sprintf("https://localhost:%d/ipfs?hash=%s", port, m.GetSticker().Hash)
|
||||
m.StickerLocalURL = s.MakeStickerURL(m.GetSticker().Hash)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,22 +15,18 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/contracts"
|
||||
"github.com/status-im/status-go/services/browsers"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/status-im/status-go/appdatabase"
|
||||
"github.com/status-im/status-go/appmetrics"
|
||||
"github.com/status-im/status-go/connection"
|
||||
"github.com/status-im/status-go/contracts"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
userimage "github.com/status-im/status-go/images"
|
||||
|
@ -56,9 +52,9 @@ import (
|
|||
"github.com/status-im/status-go/protocol/transport"
|
||||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||
"github.com/status-im/status-go/server"
|
||||
"github.com/status-im/status-go/services/browsers"
|
||||
"github.com/status-im/status-go/services/ext/mailservers"
|
||||
mailserversDB "github.com/status-im/status-go/services/mailservers"
|
||||
|
||||
"github.com/status-im/status-go/telemetry"
|
||||
)
|
||||
|
||||
|
@ -128,7 +124,7 @@ type Messenger struct {
|
|||
account *multiaccounts.Account
|
||||
mailserversDatabase *mailserversDB.Database
|
||||
browserDatabase *browsers.Database
|
||||
httpServer *server.Server
|
||||
httpServer *server.MediaServer
|
||||
quit chan struct{}
|
||||
|
||||
requestedCommunitiesLock sync.RWMutex
|
||||
|
@ -4220,7 +4216,7 @@ func (m *Messenger) MessageByChatID(chatID, cursor string, limit int) ([]*common
|
|||
}
|
||||
if m.httpServer != nil {
|
||||
for idx := range msgs {
|
||||
msgs[idx].PrepareServerURLs(m.httpServer.Port)
|
||||
msgs[idx].PrepareServerURLs(m.httpServer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4230,7 +4226,7 @@ func (m *Messenger) MessageByChatID(chatID, cursor string, limit int) ([]*common
|
|||
func (m *Messenger) prepareMessages(messages map[string]*common.Message) {
|
||||
if m.httpServer != nil {
|
||||
for idx := range messages {
|
||||
messages[idx].PrepareServerURLs(m.httpServer.Port)
|
||||
messages[idx].PrepareServerURLs(m.httpServer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ type config struct {
|
|||
clusterConfig params.ClusterConfig
|
||||
browserDatabase *browsers.Database
|
||||
torrentConfig *params.TorrentConfig
|
||||
httpServer *server.Server
|
||||
httpServer *server.MediaServer
|
||||
rpcClient *rpc.Client
|
||||
|
||||
verifyTransactionClient EthClient
|
||||
|
@ -280,7 +280,7 @@ func WithTorrentConfig(tc *params.TorrentConfig) Option {
|
|||
}
|
||||
}
|
||||
|
||||
func WithHTTPServer(s *server.Server) Option {
|
||||
func WithHTTPServer(s *server.MediaServer) Option {
|
||||
return func(c *config) error {
|
||||
c.httpServer = s
|
||||
return nil
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package protocol
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (m *Messenger) ImageServerURL() string {
|
||||
return fmt.Sprintf("https://localhost:%d/messages/", m.httpServer.Port)
|
||||
return m.httpServer.MakeImageServerURL()
|
||||
}
|
||||
|
|
110
server/certs.go
110
server/certs.go
|
@ -2,35 +2,53 @@ package server
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateX509Cert(from, to time.Time) (*x509.Certificate, error) {
|
||||
var globalCertificate *tls.Certificate = nil
|
||||
var globalPem string
|
||||
|
||||
func makeRandomSerialNumber() (*big.Int, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
return rand.Int(rand.Reader, serialNumberLimit)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func makeSerialNumberFromKey(pk *ecdsa.PrivateKey) *big.Int {
|
||||
h := sha256.New()
|
||||
h.Write(append(pk.D.Bytes(), append(pk.Y.Bytes(), pk.X.Bytes()...)...))
|
||||
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
return new(big.Int).SetBytes(h.Sum(nil))
|
||||
}
|
||||
|
||||
func GenerateX509Cert(sn *big.Int, from, to time.Time, hostname string) *x509.Certificate {
|
||||
c := &x509.Certificate{
|
||||
SerialNumber: sn,
|
||||
Subject: pkix.Name{Organization: []string{"Self-signed cert"}},
|
||||
NotBefore: from,
|
||||
NotAfter: to,
|
||||
DNSNames: []string{"localhost"},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
return template, nil
|
||||
ip := net.ParseIP(hostname)
|
||||
if ip != nil {
|
||||
c.IPAddresses = []net.IP{ip}
|
||||
} else {
|
||||
c.DNSNames = []string{hostname}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func GenerateX509PEMs(cert *x509.Certificate, key *ecdsa.PrivateKey) (certPem, keyPem []byte, err error) {
|
||||
|
@ -48,3 +66,77 @@ func GenerateX509PEMs(cert *x509.Certificate, key *ecdsa.PrivateKey) (certPem, k
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
func generateTLSCert() error {
|
||||
if globalCertificate != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(365 * 24 * time.Hour)
|
||||
|
||||
sn, err := makeRandomSerialNumber()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cert := GenerateX509Cert(sn, notBefore, notAfter, localhost)
|
||||
certPem, keyPem, err := GenerateX509PEMs(cert, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finalCert, err := tls.X509KeyPair(certPem, keyPem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
globalCertificate = &finalCert
|
||||
globalPem = string(certPem)
|
||||
return nil
|
||||
}
|
||||
|
||||
func PublicTLSCert() (string, error) {
|
||||
err := generateTLSCert()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return globalPem, nil
|
||||
}
|
||||
|
||||
func GenerateCertFromKey(pk *ecdsa.PrivateKey, ttl time.Duration, hostname string) (tls.Certificate, []byte, error) {
|
||||
// TODO fix, this isn't deterministic,
|
||||
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(ttl)
|
||||
|
||||
cert := GenerateX509Cert(makeSerialNumberFromKey(pk), notBefore, notAfter, hostname)
|
||||
certPem, keyPem, err := GenerateX509PEMs(cert, pk)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, nil, err
|
||||
}
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(certPem, keyPem)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, nil, err
|
||||
}
|
||||
|
||||
return tlsCert, certPem, nil
|
||||
}
|
||||
|
||||
// ToECDSA takes a []byte of D and uses it to create an ecdsa.PublicKey on the elliptic.P256 curve
|
||||
// this function is basically a P256 curve version of eth-node/crypto.ToECDSA without all the nice validation
|
||||
func ToECDSA(d []byte) *ecdsa.PrivateKey {
|
||||
k := new(ecdsa.PrivateKey)
|
||||
k.D = new(big.Int).SetBytes(d)
|
||||
k.PublicKey.Curve = elliptic.P256()
|
||||
|
||||
k.PublicKey.X, k.PublicKey.Y = k.PublicKey.Curve.ScalarBaseMult(d)
|
||||
return k
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil/base58"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestCerts(t *testing.T) {
|
||||
suite.Run(t, new(CertsSuite))
|
||||
}
|
||||
|
||||
const (
|
||||
X = "7744735542292224619198421067303535767629647588258222392379329927711683109548"
|
||||
Y = "6855516769916529066379811647277920115118980625614889267697023742462401590771"
|
||||
D = "38564357061962143106230288374146033267100509055924181407058066820384455255240"
|
||||
DB58 = "6jpbvo2ucrtrnpXXF4DQYuysh697isH9ppd2aT8uSRDh"
|
||||
SN = "91849736469742262272885892667727604096707836853856473239722372976236128900962"
|
||||
)
|
||||
|
||||
type CertsSuite struct {
|
||||
suite.Suite
|
||||
|
||||
X *big.Int
|
||||
Y *big.Int
|
||||
D *big.Int
|
||||
DBytes []byte
|
||||
SN *big.Int
|
||||
}
|
||||
|
||||
func (s *CertsSuite) SetupSuite() {
|
||||
var ok bool
|
||||
|
||||
s.X, ok = new(big.Int).SetString(X, 10)
|
||||
s.Require().True(ok)
|
||||
|
||||
s.Y, ok = new(big.Int).SetString(Y, 10)
|
||||
s.Require().True(ok)
|
||||
|
||||
s.D, ok = new(big.Int).SetString(D, 10)
|
||||
s.Require().True(ok)
|
||||
|
||||
s.DBytes = base58.Decode(DB58)
|
||||
s.Require().Exactly(s.D.Bytes(), s.DBytes)
|
||||
|
||||
s.SN, ok = new(big.Int).SetString(SN, 10)
|
||||
s.Require().True(ok)
|
||||
}
|
||||
|
||||
func (s *CertsSuite) Test_makeSerialNumberFromKey() {
|
||||
pk := &ecdsa.PrivateKey{
|
||||
PublicKey: ecdsa.PublicKey{
|
||||
Curve: elliptic.P256(),
|
||||
X: s.X,
|
||||
Y: s.Y,
|
||||
},
|
||||
D: s.D,
|
||||
}
|
||||
|
||||
s.Require().Zero(makeSerialNumberFromKey(pk).Cmp(s.SN))
|
||||
}
|
||||
|
||||
func (s *CertsSuite) TestToECDSA() {
|
||||
k := ToECDSA(base58.Decode(DB58))
|
||||
s.Require().NotNil(k.PublicKey.X)
|
||||
s.Require().NotNil(k.PublicKey.Y)
|
||||
|
||||
s.Require().Zero(k.PublicKey.X.Cmp(s.X))
|
||||
s.Require().Zero(k.PublicKey.Y.Cmp(s.Y))
|
||||
s.Require().Zero(k.D.Cmp(s.D))
|
||||
|
||||
b58 := base58.Encode(s.D.Bytes())
|
||||
s.Require().Equal(DB58, b58)
|
||||
}
|
||||
|
||||
func (s *CertsSuite) TestGenerateX509Cert() {
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(time.Hour)
|
||||
|
||||
c1 := GenerateX509Cert(s.SN, notBefore, notAfter, localhost)
|
||||
s.Require().Exactly([]string{localhost}, c1.DNSNames)
|
||||
s.Require().Nil(c1.IPAddresses)
|
||||
|
||||
c2 := GenerateX509Cert(s.SN, notBefore, notAfter, defaultIP.String())
|
||||
s.Require().Len(c2.IPAddresses, 1)
|
||||
s.Require().Equal(defaultIP.String(), c2.IPAddresses[0].String())
|
||||
s.Require().Nil(c2.DNSNames)
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/ipfs"
|
||||
"github.com/status-im/status-go/protocol/identity/identicon"
|
||||
"github.com/status-im/status-go/protocol/images"
|
||||
)
|
||||
|
||||
const (
|
||||
basePath = "/messages"
|
||||
identiconsPath = basePath + "/identicons"
|
||||
imagesPath = basePath + "/images"
|
||||
audioPath = basePath + "/audio"
|
||||
ipfsPath = "/ipfs"
|
||||
)
|
||||
|
||||
type HandlerPatternMap map[string]http.HandlerFunc
|
||||
|
||||
func handleIdenticon(logger *zap.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pks, ok := r.URL.Query()["publicKey"]
|
||||
if !ok || len(pks) == 0 {
|
||||
logger.Error("no publicKey")
|
||||
return
|
||||
}
|
||||
pk := pks[0]
|
||||
image, err := identicon.Generate(pk)
|
||||
if err != nil {
|
||||
logger.Error("could not generate identicon")
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
w.Header().Set("Cache-Control", "max-age:290304000, public")
|
||||
w.Header().Set("Expires", time.Now().AddDate(60, 0, 0).Format(http.TimeFormat))
|
||||
|
||||
_, err = w.Write(image)
|
||||
if err != nil {
|
||||
logger.Error("failed to write image", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleImage(db *sql.DB, logger *zap.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
messageIDs, ok := r.URL.Query()["messageId"]
|
||||
if !ok || len(messageIDs) == 0 {
|
||||
logger.Error("no messageID")
|
||||
return
|
||||
}
|
||||
messageID := messageIDs[0]
|
||||
var image []byte
|
||||
err := db.QueryRow(`SELECT image_payload FROM user_messages WHERE id = ?`, messageID).Scan(&image)
|
||||
if err != nil {
|
||||
logger.Error("failed to find image", zap.Error(err))
|
||||
return
|
||||
}
|
||||
if len(image) == 0 {
|
||||
logger.Error("empty image")
|
||||
return
|
||||
}
|
||||
mime, err := images.ImageMime(image)
|
||||
if err != nil {
|
||||
logger.Error("failed to get mime", zap.Error(err))
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", mime)
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
|
||||
_, err = w.Write(image)
|
||||
if err != nil {
|
||||
logger.Error("failed to write image", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleAudio(db *sql.DB, logger *zap.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
messageIDs, ok := r.URL.Query()["messageId"]
|
||||
if !ok || len(messageIDs) == 0 {
|
||||
logger.Error("no messageID")
|
||||
return
|
||||
}
|
||||
messageID := messageIDs[0]
|
||||
var audio []byte
|
||||
err := db.QueryRow(`SELECT audio_payload FROM user_messages WHERE id = ?`, messageID).Scan(&audio)
|
||||
if err != nil {
|
||||
logger.Error("failed to find image", zap.Error(err))
|
||||
return
|
||||
}
|
||||
if len(audio) == 0 {
|
||||
logger.Error("empty audio")
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "audio/aac")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
|
||||
_, err = w.Write(audio)
|
||||
if err != nil {
|
||||
logger.Error("failed to write audio", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleIPFS(downloader *ipfs.Downloader, logger *zap.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
hashes, ok := r.URL.Query()["hash"]
|
||||
if !ok || len(hashes) == 0 {
|
||||
logger.Error("no hash")
|
||||
return
|
||||
}
|
||||
|
||||
_, download := r.URL.Query()["download"]
|
||||
|
||||
content, err := downloader.Get(hashes[0], download)
|
||||
if err != nil {
|
||||
logger.Error("could not download hash", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "max-age:290304000, public")
|
||||
w.Header().Set("Expires", time.Now().AddDate(60, 0, 0).Format(http.TimeFormat))
|
||||
|
||||
_, err = w.Write(content)
|
||||
if err != nil {
|
||||
logger.Error("failed to write ipfs resource", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultIP = net.IP{127, 0, 0, 1}
|
||||
localhost = "localhost"
|
||||
)
|
||||
|
||||
func GetOutboundIP() (net.IP, error) {
|
||||
conn, err := net.Dial("udp", "255.255.255.255:8080")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
|
||||
return localAddr.IP, nil
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testHandler(t *testing.T) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
say, ok := r.URL.Query()["say"]
|
||||
if !ok || len(say) == 0 {
|
||||
say = append(say, "nothing")
|
||||
}
|
||||
|
||||
_, err := w.Write([]byte("Hello I like to be a tls server. You said: `" + say[0] + "` " + time.Now().String()))
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOutboundIPWithFullServerE2e(t *testing.T) {
|
||||
pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
ip, err := GetOutboundIP()
|
||||
require.NoError(t, err)
|
||||
|
||||
cert, certPem, err := GenerateCertFromKey(pk, time.Hour, ip.String())
|
||||
require.NoError(t, err)
|
||||
|
||||
s := NewPairingServer(&Config{&cert, ip.String()})
|
||||
|
||||
s.SetHandlers(HandlerPatternMap{"/hello": testHandler(t)})
|
||||
|
||||
err = s.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Give time for the sever to be ready, hacky I know
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
spew.Dump(s.MakeBaseURL().String())
|
||||
|
||||
rootCAs, err := x509.SystemCertPool()
|
||||
require.NoError(t, err)
|
||||
|
||||
ok := rootCAs.AppendCertsFromPEM(certPem)
|
||||
require.True(t, ok)
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
InsecureSkipVerify: false, // MUST BE FALSE, or the test is meaningless
|
||||
RootCAs: rootCAs,
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
b := make([]byte, 32)
|
||||
_, err = rand.Read(b)
|
||||
require.NoError(t, err)
|
||||
thing := hex.EncodeToString(b)
|
||||
|
||||
response, err := client.Get(s.MakeBaseURL().String() + "/hello?say=" + thing)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(response.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Hello I like to be a tls server. You said: `"+thing+"`", string(content[:109]))
|
||||
}
|
257
server/server.go
257
server/server.go
|
@ -2,225 +2,44 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
"net/url"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/ipfs"
|
||||
"github.com/status-im/status-go/logutils"
|
||||
"github.com/status-im/status-go/protocol/identity/identicon"
|
||||
"github.com/status-im/status-go/protocol/images"
|
||||
)
|
||||
|
||||
var globalCertificate *tls.Certificate = nil
|
||||
var globalPem string
|
||||
|
||||
func generateTLSCert() error {
|
||||
if globalCertificate != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(365 * 24 * time.Hour)
|
||||
|
||||
cert, err := GenerateX509Cert(notBefore, notAfter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certPem, keyPem, err := GenerateX509PEMs(cert, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finalCert, err := tls.X509KeyPair(certPem, keyPem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
globalCertificate = &finalCert
|
||||
globalPem = string(certPem)
|
||||
return nil
|
||||
}
|
||||
|
||||
func PublicTLSCert() (string, error) {
|
||||
err := generateTLSCert()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return globalPem, nil
|
||||
}
|
||||
|
||||
type imageHandler struct {
|
||||
db *sql.DB
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
type audioHandler struct {
|
||||
db *sql.DB
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
type identiconHandler struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
type ipfsHandler struct {
|
||||
logger *zap.Logger
|
||||
downloader *ipfs.Downloader
|
||||
}
|
||||
|
||||
func (s *identiconHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
pks, ok := r.URL.Query()["publicKey"]
|
||||
if !ok || len(pks) == 0 {
|
||||
s.logger.Error("no publicKey")
|
||||
return
|
||||
}
|
||||
pk := pks[0]
|
||||
image, err := identicon.Generate(pk)
|
||||
if err != nil {
|
||||
s.logger.Error("could not generate identicon")
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
w.Header().Set("Cache-Control", "max-age:290304000, public")
|
||||
w.Header().Set("Expires", time.Now().AddDate(60, 0, 0).Format(http.TimeFormat))
|
||||
|
||||
_, err = w.Write(image)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to write image", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *imageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
messageIDs, ok := r.URL.Query()["messageId"]
|
||||
if !ok || len(messageIDs) == 0 {
|
||||
s.logger.Error("no messageID")
|
||||
return
|
||||
}
|
||||
messageID := messageIDs[0]
|
||||
var image []byte
|
||||
err := s.db.QueryRow(`SELECT image_payload FROM user_messages WHERE id = ?`, messageID).Scan(&image)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to find image", zap.Error(err))
|
||||
return
|
||||
}
|
||||
if len(image) == 0 {
|
||||
s.logger.Error("empty image")
|
||||
return
|
||||
}
|
||||
mime, err := images.ImageMime(image)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get mime", zap.Error(err))
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", mime)
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
|
||||
_, err = w.Write(image)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to write image", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *audioHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
messageIDs, ok := r.URL.Query()["messageId"]
|
||||
if !ok || len(messageIDs) == 0 {
|
||||
s.logger.Error("no messageID")
|
||||
return
|
||||
}
|
||||
messageID := messageIDs[0]
|
||||
var audio []byte
|
||||
err := s.db.QueryRow(`SELECT audio_payload FROM user_messages WHERE id = ?`, messageID).Scan(&audio)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to find image", zap.Error(err))
|
||||
return
|
||||
}
|
||||
if len(audio) == 0 {
|
||||
s.logger.Error("empty audio")
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "audio/aac")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
|
||||
_, err = w.Write(audio)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to write audio", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ipfsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
hashes, ok := r.URL.Query()["hash"]
|
||||
if !ok || len(hashes) == 0 {
|
||||
s.logger.Error("no hash")
|
||||
return
|
||||
}
|
||||
|
||||
_, download := r.URL.Query()["download"]
|
||||
|
||||
content, err := s.downloader.Get(hashes[0], download)
|
||||
if err != nil {
|
||||
s.logger.Error("could not download hash", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "max-age:290304000, public")
|
||||
w.Header().Set("Expires", time.Now().AddDate(60, 0, 0).Format(http.TimeFormat))
|
||||
|
||||
_, err = w.Write(content)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to write ipfs resource", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Port int
|
||||
run bool
|
||||
server *http.Server
|
||||
logger *zap.Logger
|
||||
db *sql.DB
|
||||
cert *tls.Certificate
|
||||
downloader *ipfs.Downloader
|
||||
run bool
|
||||
server *http.Server
|
||||
logger *zap.Logger
|
||||
cert *tls.Certificate
|
||||
hostname string
|
||||
port int
|
||||
handlers HandlerPatternMap
|
||||
}
|
||||
|
||||
func NewServer(db *sql.DB, downloader *ipfs.Downloader) (*Server, error) {
|
||||
err := generateTLSCert()
|
||||
func NewServer(cert *tls.Certificate, hostname string) Server {
|
||||
return Server{logger: logutils.ZapLogger(), cert: cert, hostname: hostname}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Server{db: db, logger: logutils.ZapLogger(), cert: globalCertificate, Port: 0, downloader: downloader}, nil
|
||||
func (s *Server) getHost() string {
|
||||
// TODO consider returning an error if s.getPort returns `0`, as this means that the listener is not ready
|
||||
return fmt.Sprintf("%s:%d", s.hostname, s.port)
|
||||
}
|
||||
|
||||
func (s *Server) listenAndServe() {
|
||||
cfg := &tls.Config{Certificates: []tls.Certificate{*s.cert}, ServerName: "localhost", MinVersion: tls.VersionTLS12}
|
||||
cfg := &tls.Config{Certificates: []tls.Certificate{*s.cert}, ServerName: s.hostname, MinVersion: tls.VersionTLS12}
|
||||
|
||||
// in case of restart, we should use the same port as the first start in order not to break existing links
|
||||
addr := fmt.Sprintf("localhost:%d", s.Port)
|
||||
|
||||
listener, err := tls.Listen("tcp", addr, cfg)
|
||||
listener, err := tls.Listen("tcp", s.getHost(), cfg)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to start server, retrying", zap.Error(err))
|
||||
s.Port = 0
|
||||
s.port = 0
|
||||
err = s.Start()
|
||||
if err != nil {
|
||||
s.logger.Error("server start failed, giving up", zap.Error(err))
|
||||
|
@ -228,8 +47,9 @@ func (s *Server) listenAndServe() {
|
|||
return
|
||||
}
|
||||
|
||||
s.Port = listener.Addr().(*net.TCPAddr).Port
|
||||
s.port = listener.Addr().(*net.TCPAddr).Port
|
||||
s.run = true
|
||||
|
||||
err = s.server.Serve(listener)
|
||||
if err != http.ErrServerClosed {
|
||||
s.logger.Error("server failed unexpectedly, restarting", zap.Error(err))
|
||||
|
@ -243,17 +63,27 @@ func (s *Server) listenAndServe() {
|
|||
s.run = false
|
||||
}
|
||||
|
||||
func (s *Server) resetServer() {
|
||||
s.server = new(http.Server)
|
||||
}
|
||||
|
||||
func (s *Server) applyHandlers() {
|
||||
if s.server == nil {
|
||||
s.server = new(http.Server)
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
|
||||
for p, h := range s.handlers {
|
||||
mux.HandleFunc(p, h)
|
||||
}
|
||||
s.server.Handler = mux
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
handler := http.NewServeMux()
|
||||
handler.Handle("/messages/images", &imageHandler{db: s.db, logger: s.logger})
|
||||
handler.Handle("/messages/audio", &audioHandler{db: s.db, logger: s.logger})
|
||||
handler.Handle("/messages/identicons", &identiconHandler{logger: s.logger})
|
||||
handler.Handle("/ipfs", &ipfsHandler{logger: s.logger, downloader: s.downloader})
|
||||
|
||||
s.server = &http.Server{Handler: handler}
|
||||
|
||||
// Once Shutdown has been called on a server, it may not be reused;
|
||||
s.resetServer()
|
||||
s.applyHandlers()
|
||||
go s.listenAndServe()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -282,3 +112,14 @@ func (s *Server) ToBackground() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) SetHandlers(handlers HandlerPatternMap) {
|
||||
s.handlers = handlers
|
||||
}
|
||||
|
||||
func (s *Server) MakeBaseURL() *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: "https",
|
||||
Host: s.getHost(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/url"
|
||||
|
||||
"github.com/status-im/status-go/ipfs"
|
||||
)
|
||||
|
||||
type MediaServer struct {
|
||||
Server
|
||||
|
||||
db *sql.DB
|
||||
downloader *ipfs.Downloader
|
||||
}
|
||||
|
||||
// NewMediaServer returns a *MediaServer
|
||||
func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader) (*MediaServer, error) {
|
||||
err := generateTLSCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &MediaServer{
|
||||
Server: NewServer(globalCertificate, localhost),
|
||||
db: db,
|
||||
downloader: downloader,
|
||||
}
|
||||
s.SetHandlers(HandlerPatternMap{
|
||||
imagesPath: handleImage(s.db, s.logger),
|
||||
audioPath: handleAudio(s.db, s.logger),
|
||||
identiconsPath: handleIdenticon(s.logger),
|
||||
ipfsPath: handleIPFS(s.downloader, s.logger),
|
||||
})
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *MediaServer) MakeImageServerURL() string {
|
||||
u := s.MakeBaseURL()
|
||||
u.Path = basePath + "/"
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (s *MediaServer) MakeIdenticonURL(from string) string {
|
||||
u := s.MakeBaseURL()
|
||||
u.Path = identiconsPath
|
||||
u.RawQuery = url.Values{"publicKey": {from}}.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (s *MediaServer) MakeImageURL(id string) string {
|
||||
u := s.MakeBaseURL()
|
||||
u.Path = imagesPath
|
||||
u.RawQuery = url.Values{"messageId": {id}}.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (s *MediaServer) MakeAudioURL(id string) string {
|
||||
u := s.MakeBaseURL()
|
||||
u.Path = audioPath
|
||||
u.RawQuery = url.Values{"messageId": {id}}.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (s *MediaServer) MakeStickerURL(stickerHash string) string {
|
||||
u := s.MakeBaseURL()
|
||||
u.Path = ipfsPath
|
||||
u.RawQuery = url.Values{"hash": {stickerHash}}.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
type PairingServer struct {
|
||||
Server
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Cert *tls.Certificate
|
||||
Hostname string
|
||||
}
|
||||
|
||||
// NewPairingServer returns a *NewPairingServer init from the given *Config
|
||||
func NewPairingServer(config *Config) *PairingServer {
|
||||
return &PairingServer{Server: NewServer(
|
||||
config.Cert,
|
||||
config.Hostname,
|
||||
)}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestServerURLSuite(t *testing.T) {
|
||||
suite.Run(t, new(ServerURLSuite))
|
||||
}
|
||||
|
||||
type ServerURLSuite struct {
|
||||
suite.Suite
|
||||
|
||||
server *MediaServer
|
||||
serverNoPort *MediaServer
|
||||
}
|
||||
|
||||
func (s *ServerURLSuite) SetupSuite() {
|
||||
s.server = &MediaServer{Server: Server{
|
||||
hostname: defaultIP.String(),
|
||||
port: 1337,
|
||||
}}
|
||||
s.serverNoPort = &MediaServer{Server: Server{
|
||||
hostname: defaultIP.String(),
|
||||
}}
|
||||
}
|
||||
|
||||
func (s *ServerURLSuite) TestServer_MakeBaseURL() {
|
||||
s.Require().Equal("https://127.0.0.1:1337", s.server.MakeBaseURL().String())
|
||||
s.Require().Equal("https://127.0.0.1:0", s.serverNoPort.MakeBaseURL().String())
|
||||
}
|
||||
|
||||
func (s *ServerURLSuite) TestServer_MakeImageServerURL() {
|
||||
s.Require().Equal("https://127.0.0.1:1337/messages/", s.server.MakeImageServerURL())
|
||||
s.Require().Equal("https://127.0.0.1:0/messages/", s.serverNoPort.MakeImageServerURL())
|
||||
}
|
||||
|
||||
func (s *ServerURLSuite) TestServer_MakeIdenticonURL() {
|
||||
s.Require().Equal(
|
||||
"https://127.0.0.1:1337/messages/identicons?publicKey=0xdaff0d11decade",
|
||||
s.server.MakeIdenticonURL("0xdaff0d11decade"))
|
||||
s.Require().Equal(
|
||||
"https://127.0.0.1:0/messages/identicons?publicKey=0xdaff0d11decade",
|
||||
s.serverNoPort.MakeIdenticonURL("0xdaff0d11decade"))
|
||||
}
|
||||
|
||||
func (s *ServerURLSuite) TestServer_MakeImageURL() {
|
||||
s.Require().Equal(
|
||||
"https://127.0.0.1:1337/messages/images?messageId=0x10aded70ffee",
|
||||
s.server.MakeImageURL("0x10aded70ffee"))
|
||||
s.Require().Equal(
|
||||
"https://127.0.0.1:0/messages/images?messageId=0x10aded70ffee",
|
||||
s.serverNoPort.MakeImageURL("0x10aded70ffee"))
|
||||
}
|
||||
|
||||
func (s *ServerURLSuite) TestServer_MakeAudioURL() {
|
||||
s.Require().Equal(
|
||||
"https://127.0.0.1:1337/messages/audio?messageId=0xde1e7ebee71e",
|
||||
s.server.MakeAudioURL("0xde1e7ebee71e"))
|
||||
s.Require().Equal(
|
||||
"https://127.0.0.1:0/messages/audio?messageId=0xde1e7ebee71e",
|
||||
s.serverNoPort.MakeAudioURL("0xde1e7ebee71e"))
|
||||
}
|
||||
|
||||
func (s *ServerURLSuite) TestServer_MakeStickerURL() {
|
||||
s.Require().Equal(
|
||||
"https://127.0.0.1:1337/ipfs?hash=0xdeadbeef4ac0",
|
||||
s.server.MakeStickerURL("0xdeadbeef4ac0"))
|
||||
s.Require().Equal(
|
||||
"https://127.0.0.1:0/ipfs?hash=0xdeadbeef4ac0",
|
||||
s.serverNoPort.MakeStickerURL("0xdeadbeef4ac0"))
|
||||
}
|
|
@ -11,10 +11,8 @@ import (
|
|||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/server"
|
||||
"github.com/status-im/status-go/services/browsers"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"go.uber.org/zap"
|
||||
|
||||
commongethtypes "github.com/ethereum/go-ethereum/common"
|
||||
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
@ -24,7 +22,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
|
||||
"github.com/status-im/status-go/connection"
|
||||
"github.com/status-im/status-go/db"
|
||||
|
@ -39,12 +36,13 @@ import (
|
|||
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||
"github.com/status-im/status-go/protocol/transport"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/server"
|
||||
"github.com/status-im/status-go/services/browsers"
|
||||
"github.com/status-im/status-go/services/ext/mailservers"
|
||||
localnotifications "github.com/status-im/status-go/services/local-notifications"
|
||||
mailserversDB "github.com/status-im/status-go/services/mailservers"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// EnvelopeEventsHandler used for two different event types.
|
||||
|
@ -109,7 +107,7 @@ func (s *Service) GetPeer(rawURL string) (*enode.Node, error) {
|
|||
return enode.ParseV4(rawURL)
|
||||
}
|
||||
|
||||
func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, db *sql.DB, httpServer *server.Server, multiAccountDb *multiaccounts.Database, acc *multiaccounts.Account, logger *zap.Logger) error {
|
||||
func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, db *sql.DB, httpServer *server.MediaServer, multiAccountDb *multiaccounts.Database, acc *multiaccounts.Account, logger *zap.Logger) error {
|
||||
var err error
|
||||
if !s.config.ShhextConfig.PFSEnabled {
|
||||
return nil
|
||||
|
@ -394,7 +392,7 @@ func buildMessengerOptions(
|
|||
config params.NodeConfig,
|
||||
identity *ecdsa.PrivateKey,
|
||||
db *sql.DB,
|
||||
httpServer *server.Server,
|
||||
httpServer *server.MediaServer,
|
||||
rpcClient *rpc.Client,
|
||||
multiAccounts *multiaccounts.Database,
|
||||
account *multiaccounts.Account,
|
||||
|
|
|
@ -2,7 +2,6 @@ package stickers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/zenthangplus/goccm"
|
||||
|
@ -44,7 +43,7 @@ type API struct {
|
|||
|
||||
keyStoreDir string
|
||||
downloader *ipfs.Downloader
|
||||
httpServer *server.Server
|
||||
httpServer *server.MediaServer
|
||||
|
||||
ctx context.Context
|
||||
}
|
||||
|
@ -85,7 +84,7 @@ type ednStickerPackInfo struct {
|
|||
Meta ednStickerPack
|
||||
}
|
||||
|
||||
func NewAPI(ctx context.Context, acc *accounts.Database, rpcClient *rpc.Client, accountsManager *account.GethManager, rpcFiltersSrvc *rpcfilters.Service, keyStoreDir string, downloader *ipfs.Downloader, httpServer *server.Server) *API {
|
||||
func NewAPI(ctx context.Context, acc *accounts.Database, rpcClient *rpc.Client, accountsManager *account.GethManager, rpcFiltersSrvc *rpcfilters.Service, keyStoreDir string, downloader *ipfs.Downloader, httpServer *server.MediaServer) *API {
|
||||
result := &API{
|
||||
contractMaker: &contracts.ContractMaker{
|
||||
RPCClient: rpcClient,
|
||||
|
@ -327,7 +326,7 @@ func (api *API) downloadPackData(stickerPack *StickerPack, contentHash []byte, t
|
|||
}
|
||||
|
||||
func (api *API) hashToURL(hash string) string {
|
||||
return fmt.Sprintf("https://localhost:%d/ipfs?hash=%s", api.httpServer.Port, hash)
|
||||
return api.httpServer.MakeStickerURL(hash)
|
||||
}
|
||||
|
||||
func (api *API) populateStickerPackAttributes(stickerPack *StickerPack, ednSource []byte, translateHashes bool) error {
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
)
|
||||
|
||||
// NewService initializes service instance.
|
||||
func NewService(acc *accounts.Database, rpcClient *rpc.Client, accountsManager *account.GethManager, rpcFiltersSrvc *rpcfilters.Service, config *params.NodeConfig, downloader *ipfs.Downloader, httpServer *server.Server) *Service {
|
||||
func NewService(acc *accounts.Database, rpcClient *rpc.Client, accountsManager *account.GethManager, rpcFiltersSrvc *rpcfilters.Service, config *params.NodeConfig, downloader *ipfs.Downloader, httpServer *server.MediaServer) *Service {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
return &Service{
|
||||
|
@ -39,7 +39,7 @@ type Service struct {
|
|||
rpcFiltersSrvc *rpcfilters.Service
|
||||
downloader *ipfs.Downloader
|
||||
keyStoreDir string
|
||||
httpServer *server.Server
|
||||
httpServer *server.MediaServer
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue