175 lines
4.2 KiB
Go
175 lines
4.2 KiB
Go
package content
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"git.gammaspectra.live/S.O.N.G/FinalCommander/utilities"
|
|
"git.gammaspectra.live/S.O.N.G/MakyuuIchaival"
|
|
"git.gammaspectra.live/S.O.N.G/MakyuuIchaival/contentmessage"
|
|
"github.com/cloudflare/circl/sign/ed25519"
|
|
"github.com/multiformats/go-multihash"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type ServerProtocol int
|
|
|
|
const (
|
|
ProtocolOrbitalBeatV1 ServerProtocol = iota
|
|
)
|
|
|
|
type Server struct {
|
|
Index int
|
|
Address string
|
|
Protocol ServerProtocol
|
|
key []byte
|
|
Weight uint
|
|
LastCheckResult bool
|
|
lastCheckMutex sync.RWMutex
|
|
}
|
|
|
|
func NewContentServerFromArgument(arg string, index int, defaultKey []byte) (*Server, error) {
|
|
//Format Address:PORT/WEIGHT[/publicKey],
|
|
|
|
protos := strings.Split(arg, "=")
|
|
|
|
serverProtocol := ProtocolOrbitalBeatV1
|
|
|
|
serverKey := defaultKey
|
|
|
|
if len(protos) > 1 {
|
|
switch protos[0] {
|
|
case "orbt":
|
|
serverProtocol = ProtocolOrbitalBeatV1
|
|
|
|
default:
|
|
return nil, fmt.Errorf("invalid server Protocol %s", arg)
|
|
}
|
|
}
|
|
|
|
p := strings.Split(protos[len(protos)-1], "/")
|
|
if len(p) < 2 {
|
|
return nil, fmt.Errorf("invalid weighted server %s", arg)
|
|
}
|
|
|
|
weight, err := strconv.ParseUint(p[1], 10, 32)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cs := &Server{
|
|
Index: index,
|
|
Address: p[0],
|
|
Protocol: serverProtocol,
|
|
key: serverKey,
|
|
Weight: uint(weight),
|
|
LastCheckResult: false,
|
|
}
|
|
|
|
return cs, nil
|
|
}
|
|
|
|
func (s *Server) GetContentURL(content *Entry, skip []int) string {
|
|
switch s.Protocol {
|
|
case ProtocolOrbitalBeatV1:
|
|
message := contentmessage.NewContentMessageV1(content.Multihash(), ed25519.PrivateKey(s.key))
|
|
skip = append(skip, s.Index)
|
|
return s.getBaseURL(MakyuuIchaival.Bech32Encoding.EncodeToString(message.Encode()), MakyuuIchaival.Bech32Encoding.EncodeToString(utilities.EncodeIntegerList(skip)))
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func (s *Server) GetHashURL(mh multihash.Multihash, skip []int) string {
|
|
switch s.Protocol {
|
|
case ProtocolOrbitalBeatV1:
|
|
message := contentmessage.NewContentMessageV1(mh, ed25519.PrivateKey(s.key))
|
|
skip = append(skip, s.Index)
|
|
return s.getBaseURL(MakyuuIchaival.Bech32Encoding.EncodeToString(message.Encode()), MakyuuIchaival.Bech32Encoding.EncodeToString(utilities.EncodeIntegerList(skip)))
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func (s *Server) getBaseURL(args ...string) string {
|
|
return fmt.Sprintf("https://%s/%s", s.Address, strings.Join(args, "/"))
|
|
}
|
|
|
|
func (s *Server) GetCheckResult() bool {
|
|
s.lastCheckMutex.RLock()
|
|
defer s.lastCheckMutex.RUnlock()
|
|
return s.LastCheckResult
|
|
}
|
|
|
|
func (s *Server) setCheckResult(result bool) {
|
|
s.lastCheckMutex.Lock()
|
|
defer s.lastCheckMutex.Unlock()
|
|
s.LastCheckResult = result
|
|
}
|
|
|
|
func (s *Server) Check() {
|
|
customTransport := http.DefaultTransport.(*http.Transport).Clone()
|
|
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
client := &http.Client{
|
|
Transport: customTransport,
|
|
Timeout: 5 * time.Second,
|
|
}
|
|
response, err := client.Head(s.getBaseURL())
|
|
|
|
if err != nil {
|
|
s.setCheckResult(false)
|
|
return
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
if response.StatusCode != http.StatusBadRequest {
|
|
s.setCheckResult(false)
|
|
return
|
|
}
|
|
s.setCheckResult(true)
|
|
}
|
|
|
|
func (s *Server) CheckEntryKey(key *HashIdentifier) (*HashIdentifier, error) {
|
|
customTransport := http.DefaultTransport.(*http.Transport).Clone()
|
|
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
client := &http.Client{
|
|
Transport: customTransport,
|
|
Timeout: 5 * time.Second,
|
|
}
|
|
response, err := client.Head(s.GetHashURL(key.Hash(), []int{}))
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
if response.StatusCode == http.StatusNotFound {
|
|
return nil, nil
|
|
}
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("unexpected status code %d", response.StatusCode)
|
|
}
|
|
|
|
v := strings.Split(response.Header.Get("Digest"), "=")
|
|
if len(v) >= 2 && v[0] == "sha-256" {
|
|
sha, err := base64.StdEncoding.DecodeString(strings.Join(v[1:], "="))
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
mh, err := multihash.Encode(sha, multihash.SHA2_256)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
return NewHashIdentifierFromMultihash(mh), nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|