This repository has been archived by the owner on May 13, 2022. It is now read-only.
/
http_server.go
111 lines (95 loc) · 3.11 KB
/
http_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
// Commons for HTTP handling
package server
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"runtime/debug"
"time"
"github.com/hyperledger/burrow/logging"
"github.com/hyperledger/burrow/logging/structure"
"github.com/hyperledger/burrow/rpc/lib/types"
)
func StartHTTPServer(listener net.Listener, handler http.Handler, logger *logging.Logger) (*http.Server, error) {
logger.InfoMsg("Starting RPC HTTP server", "listen_address", listener.Addr().String())
server := &http.Server{Handler: RecoverAndLogHandler(handler, logger)}
go func() {
err := server.Serve(listener)
logger.TraceMsg("RPC HTTP server stopped", structure.ErrorKey, err)
}()
return server, nil
}
func WriteRPCResponseHTTP(w http.ResponseWriter, res types.RPCResponse) {
jsonBytes, err := json.MarshalIndent(res, "", " ")
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(res.Error.HTTPStatusCode())
w.Write(jsonBytes) // nolint: errcheck, gas
}
//-----------------------------------------------------------------------------
// Wraps an HTTP handler, adding error logging.
// If the inner function panics, the outer function recovers, logs, sends an
// HTTP 500 error response.
func RecoverAndLogHandler(handler http.Handler, logger *logging.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Wrap the ResponseWriter to remember the status
rww := &ResponseWriterWrapper{-1, w}
begin := time.Now()
// Common headers
origin := r.Header.Get("Origin")
rww.Header().Set("Access-Control-Allow-Origin", origin)
rww.Header().Set("Access-Control-Allow-Credentials", "true")
rww.Header().Set("Access-Control-Expose-Headers", "X-Server-Time")
rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix()))
defer func() {
// Send a 500 error if a panic happens during a handler.
// Without this, Chrome & Firefox were retrying aborted ajax requests,
// at least to my localhost.
if e := recover(); e != nil {
// If RPCResponse
if res, ok := e.(types.RPCResponse); ok {
WriteRPCResponseHTTP(rww, res)
} else {
// For the rest,
err := errors.New(fmt.Sprint(e))
logger.TraceMsg("Panic in RPC HTTP handler",
structure.ErrorKey, err,
"stack", string(debug.Stack()))
rww.WriteHeader(http.StatusInternalServerError)
WriteRPCResponseHTTP(rww, types.RPCInternalError("", err))
}
}
// Finally, log.
duration := time.Since(begin)
if rww.Status == -1 {
rww.Status = 200
}
logger.InfoMsg("Served RPC HTTP response",
"method", r.Method,
"url", r.URL,
"status", rww.Status,
"duration", duration,
"remote_address", r.RemoteAddr,
)
}()
handler.ServeHTTP(rww, r)
})
}
// Remember the status for logging
type ResponseWriterWrapper struct {
Status int
http.ResponseWriter
}
func (w *ResponseWriterWrapper) WriteHeader(status int) {
w.Status = status
w.ResponseWriter.WriteHeader(status)
}
// implements http.Hijacker
func (w *ResponseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.ResponseWriter.(http.Hijacker).Hijack()
}