/
main.go
167 lines (150 loc) · 4.42 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package main
import (
"bufio"
"errors"
"flag"
"log"
"os"
"time"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
core "github.com/ipfs/go-ipfs/core"
corehttp "github.com/ipfs/go-ipfs/core/corehttp"
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
coreunix "github.com/ipfs/go-ipfs/core/coreunix"
config "github.com/ipfs/go-ipfs/repo/config"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
)
var (
blocklistFilepath = flag.String("blocklist", "", "keys that should not be served by the gateway")
writable = flag.Bool("writable", false, "enable writing objects (with POST, PUT and DELETE)")
refreshBlockListInterval = flag.Duration("refresh-blocklist-interval", 30*time.Second, "refresh blocklist")
refreshAssetsInterval = flag.Duration("refresh-assets-interval", 30*time.Second, "refresh assets")
garbageCollectInterval = flag.Duration("gc-interval", 24*time.Hour, "frequency of repo garbage collection")
assetsPath = flag.String("assets-path", "", "if provided, periodically adds contents of path to IPFS")
host = flag.String("host", "/ip4/0.0.0.0/tcp/8080", "override the HTTP host listening address")
performGC = flag.Bool("gc", false, "perform garbage collection")
nBitsForKeypair = flag.Int("b", 1024, "number of bits for keypair (if repo is uninitialized)")
)
func main() {
flag.Parse()
if *assetsPath == "" {
log.Println("asset-path not provided. hosting gateway without file server functionality...")
}
if err := run(); err != nil {
log.Println(err)
}
}
func run() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
repoPath, err := fsrepo.BestKnownPath()
if err != nil {
return err
}
if !fsrepo.IsInitialized(repoPath) {
conf, err := config.Init(os.Stdout, *nBitsForKeypair)
if err != nil {
return err
}
if err := fsrepo.Init(repoPath, conf); err != nil {
return err
}
}
repo, err := fsrepo.Open(repoPath)
if err != nil { // owned by node
return err
}
node, err := core.NewIPFSNode(ctx, core.Online(repo))
if err != nil {
return err
}
defer node.Close()
if *performGC {
if err := runGarbageCollectorWorker(ctx, node); err != nil {
return err
}
}
if *assetsPath != "" {
if err := runFileServerWorker(ctx, node); err != nil {
return err
}
}
blocklist := &corehttp.BlockList{}
gateway := corehttp.NewGateway(corehttp.GatewayConfig{
Writable: *writable,
BlockList: blocklist,
})
if err := runBlockListWorker(blocklist, *blocklistFilepath); err != nil {
return err
}
opts := []corehttp.ServeOption{
corehttp.VersionOption(),
corehttp.IPNSHostnameOption(),
gateway.ServeOption(),
}
return corehttp.ListenAndServe(node, *host, opts...)
}
func runGarbageCollectorWorker(ctx context.Context, node *core.IpfsNode) error {
go func() {
for _ = range time.Tick(*garbageCollectInterval) {
if err := corerepo.GarbageCollect(node, ctx); err != nil {
log.Println("failed to run garbage collection", err)
}
}
}()
return nil
}
func runFileServerWorker(ctx context.Context, node *core.IpfsNode) error {
fi, err := os.Stat(*assetsPath)
if err != nil {
return err
}
if !fi.IsDir() {
return errors.New("asset path must be a directory")
}
go func() {
for _ = range time.Tick(*refreshAssetsInterval) {
_, err := coreunix.AddR(node, *assetsPath)
if err != nil {
log.Println(err)
}
}
}()
return nil
}
func runBlockListWorker(blocklist *corehttp.BlockList, filepath string) error {
if filepath == "" {
return nil
}
go func() {
for _ = range time.Tick(*refreshBlockListInterval) {
log.Println("updating the blocklist...")
func() { // in a func to allow defer f.Close()
f, err := os.Open(filepath)
if err != nil {
log.Println(err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
blocked := make(map[string]struct{}) // Implement using Bloom Filter hybrid if blocklist gets large
for scanner.Scan() {
t := scanner.Text()
blocked[t] = struct{}{}
}
// If an error occurred, do not change the existing decider. This
// is to avoid accidentally clearing the list if the deploy is
// botched.
if err := scanner.Err(); err != nil {
log.Println(err)
} else {
blocklist.SetDecider(func(s string) bool {
_, ok := blocked[s]
return !ok
})
log.Printf("updated the blocklist (%d entries)", len(blocked))
}
}()
}
}()
return nil
}