-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.go
193 lines (179 loc) · 4.93 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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package main
import (
"bufio"
"crypto/sha1"
"encoding/csv"
"encoding/hex"
"fmt"
"github.com/matteocarnelos/kadlab/kademlia"
"io/ioutil"
"math/rand"
"net"
"net/http"
"os"
"strings"
"time"
)
const BNHost = 3 // Bootstrap Node Identifier
const ListenPort = 62000
const ListenIP = "0.0.0.0"
const ListenDelaySec = 5
const CLIPrefix = ">>>"
var kdm *kademlia.Kademlia
// handleRequest treats both GET and POST requests for respectively getting the
// information and storing it
func handleRequest(w http.ResponseWriter, r *http.Request) {
ip := strings.Split(r.RemoteAddr, ":")[0]
body, _ := ioutil.ReadAll(r.Body)
fmt.Printf("\n%s -> [%s %s %s] %s\n", ip, r.Method, r.URL, r.Proto, body)
var msg string
var code int
switch r.Method {
case "GET":
hash := strings.Split(r.URL.String(), "/")[2]
if len(hash) != 40 {
code = http.StatusBadRequest
msg = "Invalid hash, please provide a valid 160-bit data hash"
break
}
if content, ok := load(hash); ok {
code = http.StatusOK
msg = content
} else {
code = http.StatusNotFound
msg = "Object not found"
}
case "POST":
if len(body) > 255 {
code = http.StatusBadRequest
msg = "Invalid object size, maximum size is 255 bytes"
break
}
hash := store(string(body))
w.Header().Set("Location", "/objects/"+hash)
code = http.StatusCreated
msg = "Object stored!"
}
w.WriteHeader(code)
fmt.Fprintln(w, msg)
fmt.Printf("[%s %d %s] %s -> %s\n\n", r.Proto, code, http.StatusText(code), msg, ip)
}
// store calls to the service layer for storing the content
func store(content string) string {
fmt.Println("Storing object...")
hash := kdm.Store([]byte(content))
fmt.Println("Object stored!")
fmt.Println()
return hash
}
// load calls to the service layer for finding the object associated
// with the hash
func load(hash string) (string, bool) {
fmt.Println("Finding object...")
if data, ok := kdm.LookupData(hash); ok {
fmt.Println("Object found!")
fmt.Println()
return data.(string), true
}
return "", false
}
func main() {
iface, _ := net.InterfaceByName("eth0") // Obtain the interface
addrs, _ := iface.Addrs()
ip := addrs[0].(*net.IPNet).IP.To4() // Obtain one address of the interface
isBN := ip[3] == BNHost
rand.Seed(int64(ip[3]))
h := sha1.New()
h.Write(ip)
id := kademlia.NewKademliaID(hex.EncodeToString(h.Sum(nil))) // Obtain the ID of the node
fmt.Printf("IP Address: %s", ip)
if isBN {
fmt.Print(" (Bootstrap Node)")
}
fmt.Printf("\nKademlia ID: %s\n", id)
fmt.Println()
// Create the kademlia object that defines the logic of the service
me := kademlia.NewContact(id, ip.String())
kdm = kademlia.NewKademlia(me)
kdm.StartListen(ListenIP, ListenPort)
delay := time.Duration(ListenDelaySec + rand.Intn(5))
time.Sleep(delay * time.Second)
if !isBN { // If it is not the Bootstrap Node
fmt.Println("Joining network...")
BNIp := net.IP{ip[0], ip[1], ip[2], BNHost} // Define the Bootstrap Node's IP
h = sha1.New()
h.Write(BNIp)
BNId := kademlia.NewKademliaID(hex.EncodeToString(h.Sum(nil)))
kdm.Net.RT.AddContact(kademlia.NewContact(BNId, BNIp.String())) // Add the BN to the routing table
kdm.LookupContact(me.ID) // Initiate a lookup
fmt.Println("Network joined!")
fmt.Println()
}
http.HandleFunc("/objects", handleRequest)
http.HandleFunc("/objects/", handleRequest)
go http.ListenAndServe(":80", nil)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() { // CLI interface
r := csv.NewReader(strings.NewReader(scanner.Text()))
r.Comma = ' '
cmdLine, _ := r.Read()
var cmd string
var args []string
if len(cmdLine) > 0 {
cmd = cmdLine[0]
}
if len(cmdLine) > 1 {
args = cmdLine[1:]
}
switch cmd {
case "put":
if len(args) != 1 {
fmt.Println("Incorrect syntax")
fmt.Println("Usage: put <data>")
break
}
if len(args[0]) > 255 {
fmt.Println("Invalid object size, maximum size is 255 bytes")
break
}
hash := store(args[0])
fmt.Printf("Object hash: %s\n\n", hash)
case "get":
if len(args) != 1 {
fmt.Println("Incorrect syntax")
fmt.Println("Usage: get <hash>")
break
}
if len(args[0]) != 40 {
fmt.Println("Invalid hash, please provide a valid 160-bit data hash")
break
}
if content, ok := load(args[0]); ok {
fmt.Printf("Object content: %s\n\n", content)
} else {
fmt.Printf("Object not found\n\n")
}
case "forget":
if len(args) != 1 {
fmt.Println("Incorrect syntax")
fmt.Println("Usage: forget <hash>")
break
}
if len(args[0]) != 40 {
fmt.Println("Invalid hash, please provide a valid 160-bit data hash")
break
}
if kdm.ForgetData(args[0]) {
fmt.Println("Object forgotten! It will be removed from the network in the next expiration period")
} else {
fmt.Printf("Operation not allowed: not the original publisher\n\n")
}
case "":
case "exit":
os.Exit(0)
default:
fmt.Printf("Command not found: %s\n", cmd)
}
fmt.Print(CLIPrefix + " ")
}
}