http.go raw
1 // Copyright (c) 2016, 2018, 2025, Oracle and/or its affiliates. All rights reserved.
2 // This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
3
4 package common
5
6 import (
7 "bytes"
8 "encoding/json"
9 "fmt"
10 "io"
11 "io/ioutil"
12 "net/http"
13 "net/url"
14 "os"
15 "reflect"
16 "regexp"
17 "strconv"
18 "strings"
19 "time"
20 )
21
22 const (
23 //UsingExpectHeaderEnvVar is the key to determine whether expect 100-continue is enabled or not
24 UsingExpectHeaderEnvVar = "OCI_GOSDK_USING_EXPECT_HEADER"
25 //EncodePathParamsEnvVar determines if special characters in path params such as / and & are URL encoded
26 EncodePathParamsEnvVar = "OCI_GOSDK_ENCODE_PATH_PARAMS"
27 // EscapeJSONToASCIIEnvVar determines if non-ASCII characters in JSON strings are escaped
28 EscapeJSONToASCIIEnvVar = "OCI_GOSDK_ESCAPE_JSON_ASCII"
29 )
30
31 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
32 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
33 //Request Marshaling
34 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
35 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
36
37 func isNil(v reflect.Value) bool {
38 return v.Kind() == reflect.Ptr && v.IsNil()
39 }
40
41 // Returns the string representation of a reflect.Value
42 // Only transforms primitive values
43 func toStringValue(v reflect.Value, field reflect.StructField) (string, error) {
44 if v.Kind() == reflect.Ptr {
45 if v.IsNil() {
46 return "", fmt.Errorf("can not marshal a nil pointer")
47 }
48 v = v.Elem()
49 }
50
51 if v.Type() == timeType {
52 t := v.Interface().(SDKTime)
53 return formatTime(t), nil
54 }
55
56 if v.Type() == sdkDateType {
57 t := v.Interface().(SDKDate)
58 return formatDate(t), nil
59 }
60
61 switch v.Kind() {
62 case reflect.Bool:
63 return strconv.FormatBool(v.Bool()), nil
64 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
65 return strconv.FormatInt(v.Int(), 10), nil
66 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
67 return strconv.FormatUint(v.Uint(), 10), nil
68 case reflect.String:
69 return v.String(), nil
70 case reflect.Float32:
71 return strconv.FormatFloat(v.Float(), 'f', -1, 32), nil
72 case reflect.Float64:
73 return strconv.FormatFloat(v.Float(), 'f', -1, 64), nil
74 default:
75 return "", fmt.Errorf("marshaling structure to a http.Request does not support field named: %s of type: %v",
76 field.Name, v.Type().String())
77 }
78 }
79
80 func addBinaryBody(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
81 readCloser, ok := value.Interface().(io.ReadCloser)
82 isMandatory, err := strconv.ParseBool(field.Tag.Get("mandatory"))
83 if err != nil {
84 return fmt.Errorf("mandatory tag is not valid for field %s", field.Name)
85 }
86
87 if isMandatory && !ok {
88 e = fmt.Errorf("body of the request is mandatory and needs to be an io.ReadCloser interface. Can not marshal body of binary request")
89 return
90 }
91
92 request.Body = readCloser
93
94 //Set the default content type to application/octet-stream if not set
95 if request.Header.Get(requestHeaderContentType) == "" {
96 request.Header.Set(requestHeaderContentType, "application/octet-stream")
97 }
98 return nil
99 }
100
101 // getTaggedNilFieldNameOrError, evaluates if a field with json and non mandatory tags is nil
102 // returns the json tag name, or an error if the tags are incorrectly present
103 func getTaggedNilFieldNameOrError(field reflect.StructField, fieldValue reflect.Value) (bool, string, error) {
104 currentTag := field.Tag
105 jsonTag := currentTag.Get("json")
106
107 if jsonTag == "" {
108 return false, "", fmt.Errorf("json tag is not valid for field %s", field.Name)
109 }
110
111 partsJSONTag := strings.Split(jsonTag, ",")
112 nameJSONField := partsJSONTag[0]
113
114 if _, ok := currentTag.Lookup("mandatory"); !ok {
115 //No mandatory field set, no-op
116 return false, nameJSONField, nil
117 }
118 isMandatory, err := strconv.ParseBool(currentTag.Get("mandatory"))
119 if err != nil {
120 return false, "", fmt.Errorf("mandatory tag is not valid for field %s", field.Name)
121 }
122
123 // If the field is marked as mandatory, no-op
124 if isMandatory {
125 return false, nameJSONField, nil
126 }
127
128 Debugf("Adjusting tag: mandatory is false and json tag is valid on field: %s", field.Name)
129
130 // If the field can not be nil, then no-op
131 if !isNillableType(&fieldValue) {
132 Debugf("WARNING json field is tagged with mandatory flags, but the type can not be nil, field name: %s", field.Name)
133 return false, nameJSONField, nil
134 }
135
136 // If field value is nil, tag it as omitEmpty
137 return fieldValue.IsNil(), nameJSONField, nil
138
139 }
140
141 // isNillableType returns true if the filed can be nil
142 func isNillableType(value *reflect.Value) bool {
143 k := value.Kind()
144 switch k {
145 case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
146 return true
147 }
148 return false
149 }
150
151 // omitNilFieldsInJSON, removes json keys whose struct value is nil, and the field is tagged with the json and
152 // mandatory:false tags
153 func omitNilFieldsInJSON(data interface{}, value reflect.Value) (interface{}, error) {
154 switch value.Kind() {
155 case reflect.Struct:
156 jsonMap := data.(map[string]interface{})
157 fieldType := value.Type()
158 for i := 0; i < fieldType.NumField(); i++ {
159 currentField := fieldType.Field(i)
160 //unexported skip
161 if currentField.PkgPath != "" {
162 continue
163 }
164
165 //Does not have json tag, no-op
166 if _, ok := currentField.Tag.Lookup("json"); !ok {
167 continue
168 }
169
170 currentFieldValue := value.Field(i)
171 ok, jsonFieldName, err := getTaggedNilFieldNameOrError(currentField, currentFieldValue)
172 if err != nil {
173 return nil, fmt.Errorf("can not omit nil fields for field: %s, due to: %s",
174 currentField.Name, err.Error())
175 }
176
177 //Delete the struct field from the json representation
178 if ok {
179 delete(jsonMap, jsonFieldName)
180 continue
181 }
182
183 // Check to make sure the field is part of the json representation of the value
184 if _, contains := jsonMap[jsonFieldName]; !contains {
185 Debugf("Field %s is not present in json, omitting", jsonFieldName)
186 continue
187 }
188
189 if currentFieldValue.Type() == timeType || currentFieldValue.Type() == timeTypePtr ||
190 currentField.Type == sdkDateType || currentField.Type == sdkDateTypePtr {
191 continue
192 }
193 // does it need to be adjusted?
194 var adjustedValue interface{}
195 adjustedValue, err = omitNilFieldsInJSON(jsonMap[jsonFieldName], currentFieldValue)
196 if err != nil {
197 return nil, fmt.Errorf("can not omit nil fields for field: %s, due to: %s",
198 currentField.Name, err.Error())
199 }
200 jsonMap[jsonFieldName] = adjustedValue
201 }
202 return jsonMap, nil
203 case reflect.Slice, reflect.Array:
204 // Special case: a []byte may have been marshalled as a string
205 if data != nil && reflect.TypeOf(data).Kind() == reflect.String && value.Type().Elem().Kind() == reflect.Uint8 {
206 return data, nil
207 }
208 jsonList, ok := data.([]interface{})
209 if !ok {
210 return nil, fmt.Errorf("can not omit nil fields, data was expected to be a not-nil list")
211 }
212 newList := make([]interface{}, len(jsonList))
213 var err error
214 for i, val := range jsonList {
215 newList[i], err = omitNilFieldsInJSON(val, value.Index(i))
216 if err != nil {
217 return nil, err
218 }
219 }
220 return newList, nil
221 case reflect.Map:
222 jsonMap, ok := data.(map[string]interface{})
223 if !ok {
224 return nil, fmt.Errorf("can not omit nil fields, data was expected to be a not-nil map")
225 }
226 newMap := make(map[string]interface{}, len(jsonMap))
227 var err error
228 for key, val := range jsonMap {
229 newMap[key], err = omitNilFieldsInJSON(val, value.MapIndex(reflect.ValueOf(key)))
230 if err != nil {
231 return nil, err
232 }
233 }
234 return newMap, nil
235 case reflect.Ptr, reflect.Interface:
236 valPtr := value.Elem()
237 return omitNilFieldsInJSON(data, valPtr)
238 default:
239 //Otherwise no-op
240 return data, nil
241 }
242 }
243
244 // removeNilFieldsInJSONWithTaggedStruct remove struct fields tagged with json and mandatory false
245 // that are nil
246 func removeNilFieldsInJSONWithTaggedStruct(rawJSON []byte, value reflect.Value) ([]byte, error) {
247 var rawInterface interface{}
248 decoder := json.NewDecoder(bytes.NewBuffer(rawJSON))
249 decoder.UseNumber()
250 var err error
251 if err = decoder.Decode(&rawInterface); err != nil {
252 return nil, err
253 }
254
255 fixedMap, err := omitNilFieldsInJSON(rawInterface, value)
256 if err != nil {
257 return nil, err
258 }
259 return json.Marshal(fixedMap)
260 }
261
262 // escapeJSONToASCII takes a JSON payload and returns an equivalent JSON where all string values are escaped to ASCII
263 func escapeJSONToASCII(rawJSON []byte) ([]byte, error) {
264 var rawInterface interface{}
265 decoder := json.NewDecoder(bytes.NewReader(rawJSON))
266 decoder.UseNumber()
267 if err := decoder.Decode(&rawInterface); err != nil {
268 return nil, err
269 }
270
271 // transform recursively visits the JSON value:
272 // - For objects (map[string[interface{}]]), recurse on each value
273 // - For arrays ([]interface{}), recurse on each element
274 // - For strings, produce a JSON-quoted ASCII-only representation
275 // - For other types, return as is
276 var transform func(interface{}) interface{}
277 transform = func(x interface{}) interface{} {
278 switch t := x.(type) {
279 case map[string]interface{}:
280 m := make(map[string]interface{}, len(t))
281 for key, val := range t {
282 m[key] = transform(val)
283 }
284 return m
285 case []interface{}:
286 s := make([]interface{}, len(t))
287 for i, val := range t {
288 s[i] = transform(val)
289 }
290 return s
291 case string:
292 // QuoteToASCII returns a 'quoted' JSON string containing only ASCII characters
293 // ex: input: "ハッピー" -> "\"\\u30cf\\u30c3\\u30d4\\u30fc\""
294 // Returns as json.RawMessage so it gets embedded directly
295 q := strconv.AppendQuoteToASCII(nil, t)
296 return json.RawMessage(q)
297 default:
298 return x
299 }
300 }
301
302 return json.Marshal(transform(rawInterface))
303 }
304
305 func addToBody(request *http.Request, value reflect.Value, field reflect.StructField, binaryBodySpecified *bool) (e error) {
306 Debugln("Marshaling to body from field:", field.Name)
307 if request.Body != nil {
308 Logf("The body of the request is already set. Structure: %s will overwrite it\n", field.Name)
309 }
310 tag := field.Tag
311 encoding := tag.Get("encoding")
312
313 if encoding == "binary" {
314 *binaryBodySpecified = true
315 return addBinaryBody(request, value, field)
316 }
317
318 rawJSON, e := json.Marshal(value.Interface())
319 if e != nil {
320 return
321 }
322 marshaled, e := removeNilFieldsInJSONWithTaggedStruct(rawJSON, value)
323 if e != nil {
324 return
325 }
326
327 if IsEnvVarTrue(EscapeJSONToASCIIEnvVar) {
328 marshaled, e = escapeJSONToASCII(marshaled)
329 if e != nil {
330 return
331 }
332 }
333
334 if defaultLogger.LogLevel() == verboseLogging {
335 Debugf("Marshaled body is: %s\n", string(marshaled))
336 }
337
338 bodyBytes := bytes.NewReader(marshaled)
339 request.ContentLength = int64(bodyBytes.Len())
340 request.Header.Set(requestHeaderContentLength, strconv.FormatInt(request.ContentLength, 10))
341 request.Header.Set(requestHeaderContentType, "application/json")
342 request.Body = ioutil.NopCloser(bodyBytes)
343 snapshot := *bodyBytes
344 request.GetBody = func() (io.ReadCloser, error) {
345 r := snapshot
346 return ioutil.NopCloser(&r), nil
347 }
348
349 return
350 }
351
352 func checkBinaryBodyLength(request *http.Request) (contentLen int64, err error) {
353 if isNopCloser(request.Body) {
354 ioReader := reflect.ValueOf(request.Body).Field(0).Interface().(io.Reader)
355 switch t := ioReader.(type) {
356 case *bytes.Reader:
357 return int64(t.Len()), nil
358 case *bytes.Buffer:
359 return int64(t.Len()), nil
360 case *strings.Reader:
361 return int64(t.Len()), nil
362 default:
363 return getNormalBinaryBodyLength(request)
364 }
365 }
366 if reflect.TypeOf(request.Body) == reflect.TypeOf((*os.File)(nil)) {
367 fi, err := (request.Body.(*os.File)).Stat()
368 if err != nil {
369 return contentLen, err
370 }
371 return fi.Size(), nil
372 }
373 return getNormalBinaryBodyLength(request)
374 }
375
376 // Helper function to judge if this struct is a nopCloser or nopCloserWriterTo
377 func isNopCloser(readCloser io.ReadCloser) bool {
378 if reflect.TypeOf(readCloser) == reflect.TypeOf(io.NopCloser(nil)) || reflect.TypeOf(readCloser) == reflect.TypeOf(io.NopCloser(struct {
379 io.Reader
380 io.WriterTo
381 }{})) {
382 return true
383 }
384 return false
385 }
386
387 func getNormalBinaryBodyLength(request *http.Request) (contentLen int64, err error) {
388 // If binary body is seekable
389 seeker := getSeeker(request.Body)
390 if seeker != nil {
391 // save the current position, calculate the unread body length and seek it back to current position
392 if curPos, err := seeker.Seek(0, io.SeekCurrent); err == nil {
393 if endPos, err := seeker.Seek(0, io.SeekEnd); err == nil {
394 contentLen = endPos - curPos
395 if _, err = seeker.Seek(curPos, io.SeekStart); err == nil {
396 return contentLen, nil
397 }
398 }
399 }
400 }
401
402 var dumpRequestBody io.ReadCloser
403 if dumpRequestBody, request.Body, err = drainBody(request.Body); err != nil {
404 return contentLen, err
405 }
406 contentBody, err := ioutil.ReadAll(dumpRequestBody)
407 if err != nil {
408 return contentLen, err
409 }
410 return int64(len(contentBody)), nil
411 }
412
413 func getSeeker(readCloser io.ReadCloser) (seeker io.Seeker) {
414 if seeker, ok := readCloser.(io.Seeker); ok {
415 return seeker
416 }
417 // the binary body is wrapped with io.NopCloser
418 if isNopCloser(readCloser) {
419 if seeker, ok := reflect.ValueOf(readCloser).Field(0).Interface().(io.Seeker); ok {
420 return seeker
421 }
422 }
423 return seeker
424 }
425
426 func addToQuery(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
427 Debugln("Marshaling to query from field: ", field.Name)
428 if request.URL == nil {
429 request.URL = &url.URL{}
430 }
431 query := request.URL.Query()
432 var queryParameterValue, queryParameterName string
433
434 if queryParameterName = field.Tag.Get("name"); queryParameterName == "" {
435 return fmt.Errorf("marshaling request to a query requires the 'name' tag for field: %s ", field.Name)
436 }
437
438 mandatory, _ := strconv.ParseBool(strings.ToLower(field.Tag.Get("mandatory")))
439
440 //If mandatory and nil. Error out
441 if mandatory && isNil(value) {
442 return fmt.Errorf("marshaling request to a header requires not nil pointer for field: %s", field.Name)
443 }
444
445 //if not mandatory and nil. Omit
446 if !mandatory && isNil(value) {
447 Debugf("Query parameter value is not mandatory and is nil pointer in field: %s. Skipping query", field.Name)
448 return
449 }
450
451 encoding := strings.ToLower(field.Tag.Get("collectionFormat"))
452 var collectionFormatStringValues []string
453 switch encoding {
454 case "csv", "multi":
455 if value.Kind() != reflect.Slice && value.Kind() != reflect.Array {
456 e = fmt.Errorf("query parameter is tagged as csv or multi yet its type is neither an Array nor a Slice: %s", field.Name)
457 break
458 }
459
460 numOfElements := value.Len()
461 collectionFormatStringValues = make([]string, numOfElements)
462 for i := 0; i < numOfElements; i++ {
463 collectionFormatStringValues[i], e = toStringValue(value.Index(i), field)
464 if e != nil {
465 break
466 }
467 }
468 queryParameterValue = strings.Join(collectionFormatStringValues, ",")
469 case "":
470 queryParameterValue, e = toStringValue(value, field)
471 default:
472 e = fmt.Errorf("encoding of type %s is not supported for query param: %s", encoding, field.Name)
473 }
474
475 if e != nil {
476 return
477 }
478
479 //check for tag "omitEmpty", this is done to accomodate unset fields that do not
480 //support an empty string: enums in query params
481 if omitEmpty, present := field.Tag.Lookup("omitEmpty"); present {
482 omitEmptyBool, _ := strconv.ParseBool(strings.ToLower(omitEmpty))
483 if queryParameterValue != "" || !omitEmptyBool {
484 addToQueryForEncoding(&query, encoding, queryParameterName, queryParameterValue, collectionFormatStringValues)
485 } else {
486 Debugf("Omitting %s, is empty and omitEmpty tag is set", field.Name)
487 }
488 } else {
489 addToQueryForEncoding(&query, encoding, queryParameterName, queryParameterValue, collectionFormatStringValues)
490 }
491
492 request.URL.RawQuery = query.Encode()
493 return
494 }
495
496 func addToQueryForEncoding(query *url.Values, encoding string, queryParameterName string, queryParameterValue string, collectionFormatStringValues []string) {
497 if encoding == "multi" {
498 for _, stringValue := range collectionFormatStringValues {
499 query.Add(queryParameterName, stringValue)
500 }
501 } else {
502 query.Set(queryParameterName, queryParameterValue)
503 }
504 }
505
506 // Adds to the path of the url in the order they appear in the structure
507 func addToPath(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
508 var additionalURLPathPart string
509 if additionalURLPathPart, e = toStringValue(value, field); e != nil {
510 return fmt.Errorf("can not marshal to path in request for field %s. Due to %s", field.Name, e.Error())
511 }
512
513 // path should not be empty for any operations
514 if len(additionalURLPathPart) == 0 {
515 return fmt.Errorf("value cannot be empty for field %s in path", field.Name)
516 }
517
518 // encode path param if EncodePathParamsEnvVar is set
519 if IsEnvVarTrue(EncodePathParamsEnvVar) {
520 additionalURLPathPart = url.PathEscape(additionalURLPathPart)
521 }
522
523 if request.URL == nil {
524 request.URL = &url.URL{}
525 request.URL.Path = ""
526 }
527 var currentURLPath = request.URL.Path
528
529 var templatedPathRegex, _ = regexp.Compile(".*{.+}.*")
530 if !templatedPathRegex.MatchString(currentURLPath) {
531 Debugln("Marshaling request to path by appending field:", field.Name)
532 allPath := []string{currentURLPath, additionalURLPathPart}
533 request.URL.Path = strings.Join(allPath, "/")
534 } else {
535 var fieldName string
536 if fieldName = field.Tag.Get("name"); fieldName == "" {
537 e = fmt.Errorf("marshaling request to path name and template requires a 'name' tag for field: %s", field.Name)
538 return
539 }
540 urlTemplate := currentURLPath
541 Debugln("Marshaling to path from field: ", field.Name, " in template: ", urlTemplate)
542 request.URL.Path = strings.Replace(urlTemplate, "{"+fieldName+"}", additionalURLPathPart, -1)
543 }
544 return
545 }
546
547 func setWellKnownHeaders(request *http.Request, headerName, headerValue string, contentLenSpecified *bool) (e error) {
548 switch strings.ToLower(headerName) {
549 case "content-length":
550 var len int
551 len, e = strconv.Atoi(headerValue)
552 if e != nil {
553 return
554 }
555 request.ContentLength = int64(len)
556 *contentLenSpecified = true
557 }
558 return nil
559 }
560
561 func addToHeader(request *http.Request, value reflect.Value, field reflect.StructField, contentLenSpecified *bool) (e error) {
562 Debugln("Marshaling to header from field: ", field.Name)
563 if request.Header == nil {
564 request.Header = http.Header{}
565 }
566
567 var headerName, headerValue string
568 if headerName = field.Tag.Get("name"); headerName == "" {
569 return fmt.Errorf("marshaling request to a header requires the 'name' tag for field: %s", field.Name)
570 }
571
572 mandatory, _ := strconv.ParseBool(strings.ToLower(field.Tag.Get("mandatory")))
573 //If mandatory and nil. Error out
574 if mandatory && isNil(value) {
575 return fmt.Errorf("marshaling request to a header requires not nil pointer for field: %s", field.Name)
576 }
577
578 // generate opc-request-id if header value is nil and header name matches
579 value = generateOpcRequestID(headerName, value)
580
581 //if not mandatory and nil. Omit
582 if !mandatory && isNil(value) {
583 Debugf("Header value is not mandatory and is nil pointer in field: %s. Skipping header", field.Name)
584 return
585 }
586
587 //Otherwise get value and set header
588 encoding := strings.ToLower(field.Tag.Get("collectionFormat"))
589 var collectionFormatStringValues []string
590 switch encoding {
591 case "csv", "multi":
592 if value.Kind() != reflect.Slice && value.Kind() != reflect.Array {
593 e = fmt.Errorf("header is tagged as csv or multi yet its type is neither an Array nor a Slice: %s", field.Name)
594 return
595 }
596
597 numOfElements := value.Len()
598 collectionFormatStringValues = make([]string, numOfElements)
599 for i := 0; i < numOfElements; i++ {
600 collectionFormatStringValues[i], e = toStringValue(value.Index(i), field)
601 if e != nil {
602 Debugf("Header element could not be marshalled to a string: %w", e)
603 return
604 }
605 }
606 headerValue = strings.Join(collectionFormatStringValues, ",")
607 default:
608 if headerValue, e = toStringValue(value, field); e != nil {
609 return
610 }
611 }
612
613 if e = setWellKnownHeaders(request, headerName, headerValue, contentLenSpecified); e != nil {
614 return
615 }
616
617 if isUniqueHeaderRequired(headerName) {
618 request.Header.Set(headerName, headerValue)
619 } else {
620 request.Header.Add(headerName, headerValue)
621 }
622 return
623 }
624
625 // Check if the header is required to be unique
626 func isUniqueHeaderRequired(headerName string) bool {
627 return strings.EqualFold(headerName, requestHeaderContentType) || strings.EqualFold(headerName, requestHeaderContentLength)
628 }
629
630 // Header collection is a map of string to string that gets rendered as individual headers with a given prefix
631 func addToHeaderCollection(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
632 Debugln("Marshaling to header-collection from field:", field.Name)
633 if request.Header == nil {
634 request.Header = http.Header{}
635 }
636
637 var headerPrefix string
638 if headerPrefix = field.Tag.Get("prefix"); headerPrefix == "" {
639 return fmt.Errorf("marshaling request to a header requires the 'prefix' tag for field: %s", field.Name)
640 }
641
642 mandatory, _ := strconv.ParseBool(strings.ToLower(field.Tag.Get("mandatory")))
643 //If mandatory and nil. Error out
644 if mandatory && isNil(value) {
645 return fmt.Errorf("marshaling request to a header requires not nil pointer for field: %s", field.Name)
646 }
647
648 //if not mandatory and nil. Omit
649 if !mandatory && isNil(value) {
650 Debugf("Header value is not mandatory and is nil pointer in field: %s. Skipping header", field.Name)
651 return
652 }
653
654 //cast to map
655 headerValues, ok := value.Interface().(map[string]string)
656 if !ok {
657 e = fmt.Errorf("header fields need to be of type map[string]string")
658 return
659 }
660
661 for k, v := range headerValues {
662 headerName := fmt.Sprintf("%s%s", headerPrefix, k)
663 request.Header.Set(headerName, v)
664 }
665 return
666 }
667
668 // Makes sure the incoming structure is able to be marshalled
669 // to a request
670 func checkForValidRequestStruct(s interface{}) (*reflect.Value, error) {
671 val := reflect.ValueOf(s)
672 for val.Kind() == reflect.Ptr {
673 if val.IsNil() {
674 return nil, fmt.Errorf("can not marshal to request a pointer to structure")
675 }
676 val = val.Elem()
677 }
678
679 if s == nil {
680 return nil, fmt.Errorf("can not marshal to request a nil structure")
681 }
682
683 if val.Kind() != reflect.Struct {
684 return nil, fmt.Errorf("can not marshal to request, expects struct input. Got %v", val.Kind())
685 }
686
687 return &val, nil
688 }
689
690 // Populates the parts of a request by reading tags in the passed structure
691 // nested structs are followed recursively depth-first.
692 func structToRequestPart(request *http.Request, val reflect.Value) (err error) {
693 typ := val.Type()
694 contentLenSpecified := false
695 binaryBodySpecified := false
696 for i := 0; i < typ.NumField(); i++ {
697 if err != nil {
698 return
699 }
700
701 sf := typ.Field(i)
702 //unexported
703 if sf.PkgPath != "" && !sf.Anonymous {
704 continue
705 }
706
707 sv := val.Field(i)
708 if method := sv.MethodByName("ValidateEnumValue"); reflect.Value.IsValid(method) {
709 if validateResult := method.Call([]reflect.Value{})[1].Interface(); validateResult != nil {
710 if err = validateResult.(error); err != nil {
711 return
712 }
713 }
714 }
715
716 tag := sf.Tag.Get("contributesTo")
717 switch tag {
718 case "header":
719 err = addToHeader(request, sv, sf, &contentLenSpecified)
720 case "header-collection":
721 err = addToHeaderCollection(request, sv, sf)
722 case "path":
723 err = addToPath(request, sv, sf)
724 case "query":
725 err = addToQuery(request, sv, sf)
726 case "body":
727 err = addToBody(request, sv, sf, &binaryBodySpecified)
728 case "":
729 Debugln(sf.Name, " does not contain contributes tag. Skipping.")
730 default:
731 err = fmt.Errorf("can not marshal field: %s. It needs to contain valid contributesTo tag", sf.Name)
732 }
733 }
734
735 // if content-length is not specified but with binary body, calculate the content length according to request body
736 if !contentLenSpecified && binaryBodySpecified && request.Body != nil && request.Body != http.NoBody {
737 contentLen, err := checkBinaryBodyLength(request)
738 if err == nil {
739 request.Header.Set(requestHeaderContentLength, strconv.FormatInt(contentLen, 10))
740 request.ContentLength = contentLen
741 }
742 }
743
744 //If content length is zero, to avoid sending transfer-coding: chunked header, need to explicitly set the body to nil/Nobody.
745 if request.Header != nil && request.Body != nil && request.Body != http.NoBody &&
746 parseContentLength(request.Header.Get(requestHeaderContentLength)) == 0 {
747 request.Body = http.NoBody
748 }
749 //If headers are and the content type was not set, we default to application/json
750 if request.Header != nil && request.Header.Get(requestHeaderContentType) == "" {
751 request.Header.Set(requestHeaderContentType, "application/json")
752 }
753
754 return
755 }
756
757 // HTTPRequestMarshaller marshals a structure to an http request using tag values in the struct
758 // The marshaller tag should like the following
759 //
760 // type A struct {
761 // ANumber string `contributesTo="query" name="number"`
762 // TheBody `contributesTo="body"`
763 // }
764 //
765 // where the contributesTo tag can be: header, path, query, body
766 // and the 'name' tag is the name of the value used in the http request(not applicable for path)
767 // If path is specified as part of the tag, the values are appened to the url path
768 // in the order they appear in the structure
769 // The current implementation only supports primitive types, except for the body tag, which needs a struct type.
770 // The body of a request will be marshaled using the tags of the structure
771 func HTTPRequestMarshaller(requestStruct interface{}, httpRequest *http.Request) (err error) {
772 var val *reflect.Value
773 if val, err = checkForValidRequestStruct(requestStruct); err != nil {
774 return
775 }
776
777 Debugln("Marshaling to Request: ", val.Type().Name())
778 err = structToRequestPart(httpRequest, *val)
779 return
780 }
781
782 // MakeDefaultHTTPRequest creates the basic http request with the necessary headers set
783 func MakeDefaultHTTPRequest(method, path string) (httpRequest http.Request) {
784 httpRequest = http.Request{
785 Proto: "HTTP/1.1",
786 ProtoMajor: 1,
787 ProtoMinor: 1,
788 Header: make(http.Header),
789 URL: &url.URL{},
790 }
791
792 httpRequest.Header.Set(requestHeaderContentLength, "0")
793 httpRequest.Header.Set(requestHeaderDate, time.Now().UTC().Format(http.TimeFormat))
794 httpRequest.Header.Set(requestHeaderOpcClientInfo, strings.Join([]string{defaultSDKMarker, Version()}, "/"))
795 httpRequest.Header.Set(requestHeaderAccept, "*/*")
796 httpRequest.Method = method
797 httpRequest.URL.Path = path
798 return
799 }
800
801 // MakeDefaultHTTPRequestWithTaggedStruct creates an http request from an struct with tagged fields, see HTTPRequestMarshaller
802 // for more information
803 func MakeDefaultHTTPRequestWithTaggedStruct(method, path string, requestStruct interface{}) (httpRequest http.Request, err error) {
804 httpRequest = MakeDefaultHTTPRequest(method, path)
805 err = HTTPRequestMarshaller(requestStruct, &httpRequest)
806 if err != nil {
807 return
808 }
809
810 return
811 }
812
813 // MakeDefaultHTTPRequestWithTaggedStructAndExtraHeaders creates an http request from an struct with tagged fields, see HTTPRequestMarshaller
814 // for more information
815 func MakeDefaultHTTPRequestWithTaggedStructAndExtraHeaders(method, path string, requestStruct interface{}, extraHeaders map[string]string) (httpRequest http.Request, err error) {
816 httpRequest, err = MakeDefaultHTTPRequestWithTaggedStruct(method, path, requestStruct)
817 for key, val := range extraHeaders {
818 httpRequest.Header.Set(key, val)
819 }
820 return
821 }
822
823 // UpdateRequestBinaryBody updates the http request's body once it is binary request and the request body is seekable
824 // if the content length is zero, no need to update request body(since it's already been set to http.Nody)
825 func UpdateRequestBinaryBody(httpRequest *http.Request, rsc *OCIReadSeekCloser) {
826 if parseContentLength(httpRequest.Header.Get(requestHeaderContentLength)) == 0 {
827 return
828 }
829 httpRequest.Body = rsc
830 return
831 }
832
833 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
834 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
835 //Request UnMarshaling
836 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
837 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
838
839 // Makes sure the incoming structure is able to be unmarshaled
840 // to a request
841 func checkForValidResponseStruct(s interface{}) (*reflect.Value, error) {
842 val := reflect.ValueOf(s)
843 for val.Kind() == reflect.Ptr {
844 if val.IsNil() {
845 return nil, fmt.Errorf("can not unmarshal to response a pointer to nil structure")
846 }
847 val = val.Elem()
848 }
849
850 if s == nil {
851 return nil, fmt.Errorf("can not unmarshal to response a nil structure")
852 }
853
854 if val.Kind() != reflect.Struct {
855 return nil, fmt.Errorf("can not unmarshal to response, expects struct input. Got %v", val.Kind())
856 }
857
858 return &val, nil
859 }
860
861 func intSizeFromKind(kind reflect.Kind) int {
862 switch kind {
863 case reflect.Int8, reflect.Uint8:
864 return 8
865 case reflect.Int16, reflect.Uint16:
866 return 16
867 case reflect.Int32, reflect.Uint32:
868 return 32
869 case reflect.Int64, reflect.Uint64:
870 return 64
871 case reflect.Int, reflect.Uint:
872 return strconv.IntSize
873 default:
874 Debugf("The type is not valid: %v. Returing int size for arch\n", kind.String())
875 return strconv.IntSize
876 }
877
878 }
879
880 func analyzeValue(stringValue string, kind reflect.Kind, field reflect.StructField) (val reflect.Value, valPointer reflect.Value, err error) {
881 switch kind {
882 case timeType.Kind():
883 var t time.Time
884 t, err = tryParsingTimeWithValidFormatsForHeaders([]byte(stringValue), field.Name)
885 if err != nil {
886 return
887 }
888 sdkTime := sdkTimeFromTime(t)
889 val = reflect.ValueOf(sdkTime)
890 valPointer = reflect.ValueOf(&sdkTime)
891 return
892 case sdkDateType.Kind():
893 var t time.Time
894 t, err = tryParsingTimeWithValidFormatsForHeaders([]byte(stringValue), field.Name)
895 if err != nil {
896 return
897 }
898 sdkDate := sdkDateFromTime(t)
899 val = reflect.ValueOf(sdkDate)
900 valPointer = reflect.ValueOf(&sdkDate)
901 return
902 case reflect.Bool:
903 var bVal bool
904 if bVal, err = strconv.ParseBool(stringValue); err != nil {
905 return
906 }
907 val = reflect.ValueOf(bVal)
908 valPointer = reflect.ValueOf(&bVal)
909 return
910 case reflect.Int:
911 size := intSizeFromKind(kind)
912 var iVal int64
913 if iVal, err = strconv.ParseInt(stringValue, 10, size); err != nil {
914 return
915 }
916 var iiVal int
917 iiVal = int(iVal)
918 val = reflect.ValueOf(iiVal)
919 valPointer = reflect.ValueOf(&iiVal)
920 return
921 case reflect.Int64:
922 size := intSizeFromKind(kind)
923 var iVal int64
924 if iVal, err = strconv.ParseInt(stringValue, 10, size); err != nil {
925 return
926 }
927 val = reflect.ValueOf(iVal)
928 valPointer = reflect.ValueOf(&iVal)
929 return
930 case reflect.Uint:
931 size := intSizeFromKind(kind)
932 var iVal uint64
933 if iVal, err = strconv.ParseUint(stringValue, 10, size); err != nil {
934 return
935 }
936 var uiVal uint
937 uiVal = uint(iVal)
938 val = reflect.ValueOf(uiVal)
939 valPointer = reflect.ValueOf(&uiVal)
940 return
941 case reflect.String:
942 val = reflect.ValueOf(stringValue)
943 valPointer = reflect.ValueOf(&stringValue)
944 case reflect.Float32:
945 var fVal float64
946 if fVal, err = strconv.ParseFloat(stringValue, 32); err != nil {
947 return
948 }
949 var ffVal float32
950 ffVal = float32(fVal)
951 val = reflect.ValueOf(ffVal)
952 valPointer = reflect.ValueOf(&ffVal)
953 return
954 case reflect.Float64:
955 var fVal float64
956 if fVal, err = strconv.ParseFloat(stringValue, 64); err != nil {
957 return
958 }
959 val = reflect.ValueOf(fVal)
960 valPointer = reflect.ValueOf(&fVal)
961 return
962 default:
963 err = fmt.Errorf("value for kind: %s not supported", kind)
964 }
965 return
966 }
967
968 // Sets the field of a struct, with the appropriate value of the string
969 // Only sets basic types
970 func fromStringValue(newValue string, val *reflect.Value, field reflect.StructField) (err error) {
971
972 if !val.CanSet() {
973 err = fmt.Errorf("can not set field name: %s of type: %v", field.Name, val.Type().String())
974 return
975 }
976
977 kind := val.Kind()
978 isPointer := false
979 if val.Kind() == reflect.Ptr {
980 isPointer = true
981 kind = field.Type.Elem().Kind()
982 }
983
984 value, valPtr, err := analyzeValue(newValue, kind, field)
985 valueType := val.Type()
986 if err != nil {
987 return
988 }
989 if !isPointer {
990 val.Set(value.Convert(valueType))
991 } else {
992 val.Set(valPtr)
993 }
994 return
995 }
996
997 // PolymorphicJSONUnmarshaler is the interface to unmarshal polymorphic json payloads
998 type PolymorphicJSONUnmarshaler interface {
999 UnmarshalPolymorphicJSON(data []byte) (interface{}, error)
1000 }
1001
1002 func valueFromPolymorphicJSON(content []byte, unmarshaler PolymorphicJSONUnmarshaler) (val interface{}, err error) {
1003 err = json.Unmarshal(content, unmarshaler)
1004 if err != nil {
1005 return
1006 }
1007 val, err = unmarshaler.UnmarshalPolymorphicJSON(content)
1008 return
1009 }
1010
1011 func valueFromJSONBody(response *http.Response, value *reflect.Value, unmarshaler PolymorphicJSONUnmarshaler) (val interface{}, err error) {
1012 //Consumes the body, consider implementing it
1013 //without body consumption
1014 var content []byte
1015 content, err = ioutil.ReadAll(response.Body)
1016 if err != nil {
1017 return
1018 }
1019
1020 if unmarshaler != nil {
1021 val, err = valueFromPolymorphicJSON(content, unmarshaler)
1022 return
1023 }
1024
1025 val = reflect.New(value.Type()).Interface()
1026 err = json.Unmarshal(content, &val)
1027 return
1028 }
1029
1030 func addFromBody(response *http.Response, value *reflect.Value, field reflect.StructField, unmarshaler PolymorphicJSONUnmarshaler) (err error) {
1031 Debugln("Unmarshalling from body to field: ", field.Name)
1032 if response.Body == nil {
1033 Debugln("Unmarshalling body skipped due to nil body content for field: ", field.Name)
1034 return nil
1035 }
1036
1037 tag := field.Tag
1038 encoding := tag.Get("encoding")
1039 var iVal interface{}
1040 switch encoding {
1041 case "binary":
1042 value.Set(reflect.ValueOf(response.Body))
1043 return
1044 case "plain-text":
1045 //Expects UTF-8
1046 byteArr, e := ioutil.ReadAll(response.Body)
1047 if e != nil {
1048 return e
1049 }
1050 str := string(byteArr)
1051 value.Set(reflect.ValueOf(&str))
1052 return
1053 default: //If the encoding is not set. we'll decode with json
1054 iVal, err = valueFromJSONBody(response, value, unmarshaler)
1055 if err != nil {
1056 return
1057 }
1058
1059 newVal := reflect.ValueOf(iVal)
1060 if newVal.Kind() == reflect.Ptr {
1061 newVal = newVal.Elem()
1062 }
1063 value.Set(newVal)
1064 return
1065 }
1066 }
1067
1068 func addFromHeader(response *http.Response, value *reflect.Value, field reflect.StructField) (err error) {
1069 Debugln("Unmarshaling from header to field: ", field.Name)
1070 var headerName string
1071 if headerName = field.Tag.Get("name"); headerName == "" {
1072 return fmt.Errorf("unmarshaling response to a header requires the 'name' tag for field: %s", field.Name)
1073 }
1074
1075 headerValue := response.Header.Get(headerName)
1076 if headerValue == "" {
1077 Debugf("Unmarshalling did not find header with name:%s", headerName)
1078 return nil
1079 }
1080
1081 if err = fromStringValue(headerValue, value, field); err != nil {
1082 return fmt.Errorf("unmarshaling response to a header failed for field %s, due to %s", field.Name,
1083 err.Error())
1084 }
1085 return
1086 }
1087
1088 func addFromHeaderCollection(response *http.Response, value *reflect.Value, field reflect.StructField) error {
1089 Debugln("Unmarshaling from header-collection to field:", field.Name)
1090 var headerPrefix string
1091 if headerPrefix = field.Tag.Get("prefix"); headerPrefix == "" {
1092 return fmt.Errorf("unmarshaling response to a header-collection requires the 'prefix' tag for field: %s", field.Name)
1093 }
1094
1095 mapCollection := make(map[string]string)
1096 for name, value := range response.Header {
1097 nameLowerCase := strings.ToLower(name)
1098 if strings.HasPrefix(nameLowerCase, headerPrefix) {
1099 headerNoPrefix := strings.TrimPrefix(nameLowerCase, headerPrefix)
1100 mapCollection[headerNoPrefix] = value[0]
1101 }
1102 }
1103
1104 Debugln("Marshalled header collection is:", mapCollection)
1105 value.Set(reflect.ValueOf(mapCollection))
1106 return nil
1107 }
1108
1109 // Populates a struct from parts of a request by reading tags of the struct
1110 func responseToStruct(response *http.Response, val *reflect.Value, unmarshaler PolymorphicJSONUnmarshaler) (err error) {
1111 typ := val.Type()
1112 for i := 0; i < typ.NumField(); i++ {
1113 if err != nil {
1114 return
1115 }
1116
1117 sf := typ.Field(i)
1118
1119 //unexported
1120 if sf.PkgPath != "" {
1121 continue
1122 }
1123
1124 sv := val.Field(i)
1125 tag := sf.Tag.Get("presentIn")
1126 switch tag {
1127 case "header":
1128 err = addFromHeader(response, &sv, sf)
1129 case "header-collection":
1130 err = addFromHeaderCollection(response, &sv, sf)
1131 case "body":
1132 err = addFromBody(response, &sv, sf, unmarshaler)
1133 case "":
1134 Debugln(sf.Name, " does not contain presentIn tag. Skipping")
1135 default:
1136 err = fmt.Errorf("can not unmarshal field: %s. It needs to contain valid presentIn tag", sf.Name)
1137 }
1138 }
1139 return
1140 }
1141
1142 // UnmarshalResponse hydrates the fields of a struct with the values of a http response, guided
1143 // by the field tags. The directive tag is "presentIn" and it can be either
1144 // - "header": Will look for the header tagged as "name" in the headers of the struct and set it value to that
1145 // - "body": It will try to marshal the body from a json string to a struct tagged with 'presentIn: "body"'.
1146 //
1147 // Further this method will consume the body it should be safe to close it after this function
1148 // Notice the current implementation only supports native types:int, strings, floats, bool as the field types
1149 func UnmarshalResponse(httpResponse *http.Response, responseStruct interface{}) (err error) {
1150
1151 // Check for text/event-stream content type, and return without unmarshalling
1152 if httpResponse != nil && httpResponse.Header != nil && strings.ToLower(httpResponse.Header.Get("content-type")) == "text/event-stream" {
1153 return
1154 }
1155
1156 var val *reflect.Value
1157 if val, err = checkForValidResponseStruct(responseStruct); err != nil {
1158 return
1159 }
1160
1161 if err = responseToStruct(httpResponse, val, nil); err != nil {
1162 return
1163 }
1164
1165 return nil
1166 }
1167
1168 // UnmarshalResponseWithPolymorphicBody similar to UnmarshalResponse but assumes the body of the response
1169 // contains polymorphic json. This function will use the unmarshaler argument to unmarshal json content
1170 func UnmarshalResponseWithPolymorphicBody(httpResponse *http.Response, responseStruct interface{}, unmarshaler PolymorphicJSONUnmarshaler) (err error) {
1171
1172 var val *reflect.Value
1173 if val, err = checkForValidResponseStruct(responseStruct); err != nil {
1174 return
1175 }
1176
1177 if err = responseToStruct(httpResponse, val, unmarshaler); err != nil {
1178 return
1179 }
1180
1181 return nil
1182 }
1183
1184 // generate request id if user not provided and for each retry operation re-gen a new request id
1185 func generateOpcRequestID(headerName string, value reflect.Value) (newValue reflect.Value) {
1186 newValue = value
1187 isNilValue := isNil(newValue)
1188 isOpcRequestIDHeader := headerName == requestHeaderOpcRequestID || headerName == requestHeaderOpcClientRequestID
1189
1190 if isNilValue && isOpcRequestIDHeader {
1191 requestID, err := generateRandUUID()
1192
1193 if err != nil {
1194 // this will not fail the request, just skip add opc-request-id
1195 Debugf("unable to generate opc-request-id. %s", err.Error())
1196 } else {
1197 newValue = reflect.ValueOf(String(requestID))
1198 Debugf("add request id for header: %s, with value: %s", headerName, requestID)
1199 }
1200 }
1201
1202 return
1203 }
1204