-
Notifications
You must be signed in to change notification settings - Fork 3
/
server.go
113 lines (91 loc) · 2.49 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
// Package hpc implements a Gorilla RPC Codec which implements HTTP RPC via "/<service>/<method>" and JSON bodies.
package hpc
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"github.com/gorilla/rpc/v2"
"github.com/zhgo/nameconv"
)
// Errors.
var (
ErrServiceMissing = errors.New("hpc: service name missing")
ErrMethodMissing = errors.New("hpc: method name missing")
)
// StatusError represents an error with HTTP status code, it is
// native to the API and may be exposed in the response.
type StatusError interface {
StatusCode() int
error
}
// Status error.
type statusError struct {
Message string `json:"error"`
Status int `json:"-"`
}
// Error implements error.
func (e *statusError) Error() string {
return e.Message
}
// StatusCode implements StatusError.
func (e *statusError) StatusCode() int {
return e.Status
}
// NewError with status code and message. Use this for public errors.
func NewError(status int, msg string) StatusError {
return &statusError{
Message: msg,
Status: status,
}
}
// Codec implements Gorilla's rpc.Codec.
type Codec struct{}
// NewCodec returns a Gorilla RPC codec.
func NewCodec() *Codec {
return &Codec{}
}
// NewRequest implements rpc.Codec.
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
return &codecRequest{r}
}
// Codec request.
type codecRequest struct {
r *http.Request
}
// Method parses the service name and method from the request url.
func (c *codecRequest) Method() (string, error) {
p := strings.Split(c.r.URL.Path, "/")
if len(p) < 2 {
return "", ErrServiceMissing
}
if len(p) < 3 {
return "", ErrMethodMissing
}
service := nameconv.UnderscoreToCamelcase(p[1], true)
method := nameconv.UnderscoreToCamelcase(p[2], true)
s := fmt.Sprintf("%s.%s", service, method)
return s, nil
}
// ReadRequest reads the JSON request body.
func (c *codecRequest) ReadRequest(args interface{}) error {
return json.NewDecoder(c.r.Body).Decode(args)
}
// WriteResponse writes the JSON response body.
func (c *codecRequest) WriteResponse(w http.ResponseWriter, reply interface{}) {
json.NewEncoder(w).Encode(reply)
}
// WriteError handles writing request errors.
func (c *codecRequest) WriteError(w http.ResponseWriter, status int, err error) {
w.Header().Set("Content-Type", "application/json")
if err, ok := err.(StatusError); ok {
w.WriteHeader(err.StatusCode())
json.NewEncoder(w).Encode(err)
return
}
w.WriteHeader(status)
json.NewEncoder(w).Encode(&statusError{
Message: http.StatusText(status),
})
}