Archived
70 lines
1.8 KiB
Go
70 lines
1.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
propagateTimeout = 10 * time.Second
|
|
)
|
|
|
|
// Propagator handles forwarding data to peer nodes.
|
|
type Propagator struct {
|
|
peers []string
|
|
client *http.Client
|
|
}
|
|
|
|
// NewPropagator creates a new Propagator instance.
|
|
func NewPropagator(peers []string) *Propagator {
|
|
return &Propagator{
|
|
peers: peers,
|
|
client: &http.Client{
|
|
Timeout: propagateTimeout,
|
|
},
|
|
}
|
|
}
|
|
|
|
// PropagateToAllPeers sends the data to all configured peers serially.
|
|
// Errors are logged but do not stop propagation to remaining peers.
|
|
func (p *Propagator) PropagateToAllPeers(data []byte, filename string, signature []byte, timestamp int64) {
|
|
for _, peer := range p.peers {
|
|
if err := p.propagateToPeer(peer, data, filename, signature, timestamp); err != nil {
|
|
log.Printf("Failed to propagate to peer %s: %v", peer, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// propagateToPeer sends data to a single peer.
|
|
func (p *Propagator) propagateToPeer(peer string, data []byte, filename string, signature []byte, timestamp int64) error {
|
|
url := peer + "/data"
|
|
|
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
req.Header.Set("X-Filename", filename)
|
|
req.Header.Set("X-Signature", base64.StdEncoding.EncodeToString(signature))
|
|
req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10))
|
|
req.Header.Set("Content-Type", "application/octet-stream")
|
|
|
|
resp, err := p.client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("request failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Accept 200 (stored) or 409 (already have newer) as success
|
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusConflict {
|
|
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
}
|