forked from sonic-net/sonic-mgmt-framework
-
Notifications
You must be signed in to change notification settings - Fork 16
/
context.go
238 lines (192 loc) · 6.75 KB
/
context.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
////////////////////////////////////////////////////////////////////////////////
// //
// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or //
// its subsidiaries. //
// //
// Licensed under the Apache License, Version 2.0 (the "License"); //
// you may not use this file except in compliance with the License. //
// You may obtain a copy of the License at //
// //
// http://www.apache.org/licenses/LICENSE-2.0 //
// //
// Unless required by applicable law or agreed to in writing, software //
// distributed under the License is distributed on an "AS IS" BASIS, //
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
// See the License for the specific language governing permissions and //
// limitations under the License. //
// //
////////////////////////////////////////////////////////////////////////////////
package server
import (
"context"
"fmt"
"mime"
"net/http"
"regexp"
"sync/atomic"
)
// AuthInfo holds data about the authenticated user
type AuthInfo struct {
// Username
User string
// Roles
Roles []string
}
// RequestContext holds metadata about REST request.
type RequestContext struct {
// Unique reqiest id
ID string
// Name represents the operationId from OpenAPI spec
Name string
// "consumes" and "produces" data from OpenAPI spec
Consumes MediaTypes
Produces MediaTypes
// Model holds pointer to the OpenAPI data model object for
// the body. When set, the request handler can validate the
// request payload by loading the body into this model object.
Model interface{}
// PMap is the mapping of URI parameter names to actual yang
// leaf names. Yang xpaths can have duplicate parameter names,
// which is not supported by swagger and mux libraries. We
// work around it by assigning different parameter names in
// swagger and map them back to yang names while converting
// REST paths to TransLib paths.
PMap NameMap
// stats is the apiStats object from the context
stats *apiStats
// route contains current route information
route *routeMatchInfo
// Auth contains the authorized user information
Auth AuthInfo
ClientAuth UserAuth
}
type contextkey int
const (
requestContextKey contextkey = iota + 1
statsContextKey
routeContextKey
)
// Request Id generator
var requestCounter uint64
// GetContext function returns the RequestContext object for a
// HTTP request. RequestContext is maintained as a context value of
// the request. Creates a new RequestContext object is not already
// available; in which case this function also creates a copy of
// the HTTP request object with new context.
func GetContext(r *http.Request) (*RequestContext, *http.Request) {
cv := r.Context().Value(requestContextKey)
if cv != nil {
return cv.(*RequestContext), r
}
rc := new(RequestContext)
rc.ID = fmt.Sprintf("REST-%v", atomic.AddUint64(&requestCounter, 1))
rc.stats = getApiStats(r)
r = r.WithContext(context.WithValue(r.Context(), requestContextKey, rc))
return rc, r
}
///////////
// MediaType represents the parsed media type value. Includes
// a MIME type string and optional parameters.
type MediaType struct {
Type string
Params map[string]string
TypePrefix string
TypeSuffix string
TypeMiddle string
}
// mediaTypeExpr is the regex to extract parts from media type string.
var mediaTypeExpr = regexp.MustCompile(`([^/]+)(?:/(?:([^+]+)\+)?(.+))?`)
// Parse function parses a full media type value with parameters
// into this MediaType object.
func parseMediaType(value string) (*MediaType, error) {
if value == "" {
return nil, nil
}
mtype, params, err := mime.ParseMediaType(value)
if err != nil {
return nil, err
}
// Extract parts from the mime type
parts := mediaTypeExpr.FindStringSubmatch(mtype)
if parts[3] == "*" && parts[2] == "" { // for patterns like "text/*"
parts[2] = "*"
}
return &MediaType{Type: mtype, Params: params,
TypePrefix: parts[1], TypeMiddle: parts[2], TypeSuffix: parts[3]}, nil
}
// Format function returns the full media type string - including
// MIME type and parameters.
func (m *MediaType) Format() string {
return mime.FormatMediaType(m.Type, m.Params)
}
// Matches verifies if this Mediatype matches the another MediaType.
func (m *MediaType) Matches(other *MediaType) bool {
return m.Type == other.Type ||
(matchPart(m.TypePrefix, other.TypePrefix) &&
matchPart(m.TypeMiddle, other.TypeMiddle) &&
matchPart(m.TypeSuffix, other.TypeSuffix))
}
// isJSON function checks if this media type represents a json
// content. Uses the suffix part of media type string.
func (m *MediaType) isJSON() bool {
return m.TypeSuffix == "json"
}
func matchPart(x, y string) bool {
return x == y || x == "*" || y == "*"
}
//////////
// MediaTypes is a collection of parsed media type values
type MediaTypes []MediaType
// Add function parses and adds a media type to the MediaTypes
// object. Any parameters in the media type value are ignored.
func (m *MediaTypes) Add(mimeType string) error {
mtype, err := parseMediaType(mimeType)
if err == nil {
*m = append(*m, *mtype)
}
return err
}
// Contains function checks if a given media type value is
// present in the ContentTypes. Ignores the media type parameters.
func (m *MediaTypes) Contains(mimeType string) bool {
t, _, _ := mime.ParseMediaType(mimeType)
for _, entry := range *m {
if entry.Type == t {
return true
}
}
return false
}
// GetMatching returns registered full content type value
// matching a given hint.
func (m *MediaTypes) GetMatching(mimeType string) MediaTypes {
mtype, err := parseMediaType(mimeType)
if err != nil {
return nil // TODO return error
}
var matchList MediaTypes
for _, entry := range *m {
if entry.Matches(mtype) {
matchList = append(matchList, entry)
}
}
return matchList
}
func (m MediaTypes) String() string {
types := make([]string, 0, len(m))
for _, entry := range m {
types = append(types, entry.Type)
}
return fmt.Sprintf("%v", types)
}
//////////
// NameMap is a simple mapping of names (string to string)
type NameMap map[string]string
// Get function returns the mapped name for a given name.
// Returns given name itself if no mapping exists.
func (m *NameMap) Get(name string) string {
if mappedName, ok := (*m)[name]; ok {
return mappedName
}
return name
}