-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
181 lines (150 loc) · 4.57 KB
/
server.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
package copperchain
import (
"bufio"
"encoding/json"
"fmt"
"github.com/gorilla/mux"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"time"
)
type ServerOptions struct {
Host string
Port int
}
const DEFAULT_HTTP_PORT int = 8080
const DEFAULT_TCP_PORT int = 9000
var defaultServerOptions ServerOptions = ServerOptions{
Port: 8080,
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* T C P S E R V E R *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var bcServer chan BlockChain
func RunTcpServer(options ServerOptions) error {
bcServer = make(chan BlockChain)
// Validate the parameters
if options.Host == "" {
fmt.Printf("empty host passed for server, Go will default to localhost.")
}
if options.Port < 1 {
fmt.Printf("invalid port '%d' passed in server, defaulting to port %d.", defaultServerOptions.Port)
options.Port = DEFAULT_TCP_PORT
}
server, err := net.Listen("tcp", fmt.Sprintf("%s:%d", options.Host, options.Port))
if err != nil {
return err
}
defer server.Close()
for {
// everytime the server gets a new connection request
conn, err := server.Accept()
if err != nil {
return err
}
go handleTcpConn(conn)
}
}
func handleTcpConn(conn net.Conn) {
defer conn.Close()
io.WriteString(conn, "Please provide your data:")
scanner := bufio.NewScanner(conn)
// start up the process that sends myChain to connections every x seconds
go func() {
for {
time.Sleep(30 * time.Second)
myChainBytes, err := json.Marshal(myChain)
if err != nil {
log.Fatal(err)
}
io.WriteString(conn, string(myChainBytes))
}
}()
// take in data from the TCP connection
func() {
for scanner.Scan() {
fmt.Printf("Data received from %s", conn.LocalAddr())
data := scanner.Bytes()
var blockData BlockData
err := json.Unmarshal(data, &blockData)
if err != nil {
log.Printf("Error while unmarshalling scanned data: %v", err)
continue
}
err = AddToMyChain(blockData)
if err != nil {
log.Printf("Error while adding data to chain: %v", err)
continue
}
bcServer <- myChain.Chain
io.WriteString(conn, "Please provide your data:")
}
}()
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* H T T P S E R V E R *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// RunServer starts a webserver on the address provided and listens on two endpoints:
// 1. GET /: serves the blockchain
// 2. POST /: adds data to the blockchain. It accepts a json encoded BlockData as http body.
func RunHttpServer(options ServerOptions) error {
// Validate the parameters
if options.Host == "" {
fmt.Printf("empty host passed for server, Go will default to localhost.")
}
if options.Port < 1 {
fmt.Printf("invalid port '%d' passed in server, defaulting to port %d.", defaultServerOptions.Port)
options.Port = DEFAULT_HTTP_PORT
}
// Start the webserver
router := mux.NewRouter()
router.HandleFunc("/", HandleGetBlockChain).Methods("GET")
router.HandleFunc("/", HandleWriteBlock).Methods("POST")
server := &http.Server{
Addr: fmt.Sprintf("%s:%d", options.Host, options.Port),
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
fmt.Printf("Server listening on %s...\n", server.Addr)
return server.ListenAndServe()
}
// HandleGetBlockChain listens for GET requests and response with the
// blockchain. This is basically a HTTP getter for blockchain.
func HandleGetBlockChain(w http.ResponseWriter, r *http.Request) {
// Get the BlockChain, json encode it and send it.
chain := GetMyChain()
response, err := json.Marshal(chain)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(response)
}
// HandleWriteBlock listens for POST requests with payload that is
// a valid BlockData. It creates a new Block for that data and adds
// it to the blockchain.
func HandleWriteBlock(w http.ResponseWriter, r *http.Request) {
// parse the request body to get the blockdata
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer r.Body.Close()
var blockData BlockData
err = json.Unmarshal(body, &blockData)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// add the data to the blockchain
err = AddToMyChain(blockData)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}