Skip to content

Commit c165542

Browse files
committed
chore: move Message/MessageConfig -> message.go
1 parent d025787 commit c165542

File tree

2 files changed

+248
-240
lines changed

2 files changed

+248
-240
lines changed

message.go

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
package httpsign
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"net/http"
7+
"net/url"
8+
"time"
9+
)
10+
11+
// MessageDetails aggregates the details of a signed message, for a given signature
12+
type MessageDetails struct {
13+
KeyID string
14+
Alg string
15+
Fields Fields
16+
Created *time.Time
17+
Expires *time.Time
18+
Nonce *string
19+
Tag *string
20+
}
21+
22+
// Message represents a parsed HTTP message ready for signature verification.
23+
type Message struct {
24+
headers http.Header
25+
trailers http.Header
26+
body *io.ReadCloser
27+
28+
method string
29+
url *url.URL
30+
authority string
31+
scheme string
32+
statusCode *int
33+
assocReq *Message
34+
}
35+
36+
// NewMessage constructs a new Message from the provided config.
37+
func NewMessage(config *MessageConfig) (*Message, error) {
38+
if config == nil {
39+
config = NewMessageConfig()
40+
}
41+
42+
hasRequest := config.method != ""
43+
hasResponse := config.statusCode != nil
44+
45+
if !hasRequest && !hasResponse {
46+
return nil, fmt.Errorf("message config must have either method (for request) or status code (for response)")
47+
}
48+
49+
if hasRequest && hasResponse {
50+
return nil, fmt.Errorf("message config cannot have both request and response fields set")
51+
}
52+
53+
if hasRequest {
54+
if config.headers == nil {
55+
return nil, fmt.Errorf("request message must have headers")
56+
}
57+
}
58+
59+
if hasResponse {
60+
if config.headers == nil {
61+
return nil, fmt.Errorf("response message must have headers")
62+
}
63+
}
64+
65+
var assocReq *Message
66+
if config.assocReq != nil {
67+
method := config.assocReq.method
68+
u := config.assocReq.url
69+
headers := config.assocReq.headers
70+
authority := config.assocReq.authority
71+
scheme := config.assocReq.scheme
72+
if method == "" || u == nil || headers == nil || authority == "" || scheme == "" {
73+
return nil, fmt.Errorf("invalid associated request")
74+
}
75+
assocReq = &Message{
76+
method: method,
77+
url: u,
78+
headers: headers,
79+
authority: authority,
80+
scheme: scheme,
81+
}
82+
}
83+
84+
return &Message{
85+
headers: config.headers,
86+
trailers: config.trailers,
87+
body: config.body,
88+
method: config.method,
89+
url: config.url,
90+
authority: config.authority,
91+
scheme: config.scheme,
92+
statusCode: config.statusCode,
93+
assocReq: assocReq,
94+
}, nil
95+
}
96+
97+
// MessageConfig configures a Message for signature verification.
98+
type MessageConfig struct {
99+
method string
100+
url *url.URL
101+
headers http.Header
102+
trailers http.Header
103+
body *io.ReadCloser
104+
authority string
105+
scheme string
106+
107+
statusCode *int
108+
109+
assocReq *MessageConfig
110+
}
111+
112+
// NewMessageConfig returns a new MessageConfig.
113+
func NewMessageConfig() *MessageConfig {
114+
return &MessageConfig{}
115+
}
116+
117+
func (b *MessageConfig) WithMethod(method string) *MessageConfig {
118+
b.method = method
119+
return b
120+
}
121+
122+
func (b *MessageConfig) WithURL(u *url.URL) *MessageConfig {
123+
b.url = u
124+
return b
125+
}
126+
127+
func (b *MessageConfig) WithHeaders(headers http.Header) *MessageConfig {
128+
b.headers = headers
129+
return b
130+
}
131+
132+
func (b *MessageConfig) WithTrailers(trailers http.Header) *MessageConfig {
133+
b.trailers = trailers
134+
return b
135+
}
136+
137+
func (b *MessageConfig) WithBody(body *io.ReadCloser) *MessageConfig {
138+
b.body = body
139+
return b
140+
}
141+
142+
func (b *MessageConfig) WithAuthority(authority string) *MessageConfig {
143+
b.authority = authority
144+
return b
145+
}
146+
147+
func (b *MessageConfig) WithScheme(scheme string) *MessageConfig {
148+
b.scheme = scheme
149+
return b
150+
}
151+
152+
func (b *MessageConfig) WithStatusCode(statusCode int) *MessageConfig {
153+
b.statusCode = &statusCode
154+
return b
155+
}
156+
157+
func (b *MessageConfig) WithAssociatedRequest(method string, u *url.URL, headers http.Header, authority, scheme string) *MessageConfig {
158+
b.assocReq = &MessageConfig{
159+
method: method,
160+
url: u,
161+
headers: headers,
162+
authority: authority,
163+
scheme: scheme,
164+
}
165+
return b
166+
}
167+
168+
func (b *MessageConfig) WithRequest(req *http.Request) *MessageConfig {
169+
if req == nil {
170+
return b
171+
}
172+
173+
scheme := "http"
174+
if req.TLS != nil {
175+
scheme = "https"
176+
}
177+
178+
return b.
179+
WithMethod(req.Method).
180+
WithURL(req.URL).
181+
WithHeaders(req.Header).
182+
WithTrailers(req.Trailer).
183+
WithBody(&req.Body).
184+
WithAuthority(req.Host).
185+
WithScheme(scheme)
186+
}
187+
188+
func (b *MessageConfig) WithResponse(res *http.Response, req *http.Request) *MessageConfig {
189+
if res == nil {
190+
return b
191+
}
192+
193+
b = b.
194+
WithStatusCode(res.StatusCode).
195+
WithHeaders(res.Header).
196+
WithTrailers(res.Trailer).
197+
WithBody(&res.Body)
198+
199+
if req != nil {
200+
scheme := "http"
201+
if req.TLS != nil {
202+
scheme = "https"
203+
}
204+
b = b.WithAssociatedRequest(req.Method, req.URL, req.Header, req.Host, scheme)
205+
}
206+
207+
return b
208+
}
209+
210+
// Verify verifies a signature on this message.
211+
func (m *Message) Verify(signatureName string, verifier Verifier) (*MessageDetails, error) {
212+
_, psiSig, err := verifyDebug(signatureName, verifier, m)
213+
if err != nil {
214+
return nil, err
215+
}
216+
217+
details := &MessageDetails{
218+
Fields: psiSig.fields,
219+
}
220+
221+
if keyID, ok := psiSig.params["keyid"].(string); ok {
222+
details.KeyID = keyID
223+
}
224+
225+
if alg, ok := psiSig.params["alg"].(string); ok {
226+
details.Alg = alg
227+
}
228+
229+
if created, ok := psiSig.params["created"].(int64); ok {
230+
t := time.Unix(created, 0)
231+
details.Created = &t
232+
}
233+
234+
if expires, ok := psiSig.params["expires"].(int64); ok {
235+
t := time.Unix(expires, 0)
236+
details.Expires = &t
237+
}
238+
239+
if nonce, ok := psiSig.params["nonce"].(string); ok {
240+
details.Nonce = &nonce
241+
}
242+
243+
if tag, ok := psiSig.params["tag"].(string); ok {
244+
details.Tag = &tag
245+
}
246+
247+
return details, nil
248+
}

0 commit comments

Comments
 (0)