-
Notifications
You must be signed in to change notification settings - Fork 126
/
send.go
130 lines (112 loc) · 3.5 KB
/
send.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
/*
Copyright (c) 2018 Red Hat, Inc.
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.
*/
// This file contains the implementation of the methods of the connection that are used to send HTTP
// requests and receive HTTP responses.
package sdk
import (
"context"
"fmt"
"net/http"
"path"
"github.com/openshift-online/ocm-sdk-go/internal"
)
// RoundTrip is the implementation of the http.RoundTripper interface.
func (c *Connection) RoundTrip(request *http.Request) (response *http.Response, err error) {
// Check if the connection is closed:
err = c.checkClosed()
if err != nil {
return
}
// Get the context from the request:
ctx := request.Context()
// Check the request URL:
if request.URL.Path == "" {
err = fmt.Errorf("request path is mandatory")
return
}
if request.URL.Scheme != "" || request.URL.Host != "" || !path.IsAbs(request.URL.Path) {
err = fmt.Errorf("request URL '%s' isn't absolute", request.URL)
return
}
// Select the target server add the base URL to the request URL:
server, err := c.selectServer(ctx, request)
if err != nil {
return
}
request.URL = server.URL.ResolveReference(request.URL)
// Check the request method and body:
switch request.Method {
case http.MethodGet, http.MethodDelete:
if request.Body != nil {
c.logger.Warn(ctx,
"Request body is not allowed for the '%s' method",
request.Method,
)
}
case http.MethodPost, http.MethodPatch, http.MethodPut:
// POST and PATCH and PUT don't need to have a body. It is up to the server to decide if
// this is acceptable.
default:
err = fmt.Errorf("method '%s' is not allowed", request.Method)
return
}
// Add the default headers:
if request.Header == nil {
request.Header = make(http.Header)
}
if c.agent != "" {
request.Header.Set("User-Agent", c.agent)
}
switch request.Method {
case http.MethodPost, http.MethodPatch, http.MethodPut:
request.Header.Set("Content-Type", "application/json")
}
request.Header.Set("Accept", "application/json")
// Select the client:
client, err := c.clientSelector.Select(ctx, server)
if err != nil {
return
}
// Send the request:
response, err = client.Do(request)
if err != nil {
return
}
// Check that the response content type is JSON:
err = internal.CheckContentType(response)
if err != nil {
return
}
return
}
// selectServer selects the server that should be used for the given request, according its path and
// the alternative URLs configured when the connection was created.
func (c *Connection) selectServer(ctx context.Context,
request *http.Request) (base *internal.ServerAddress, err error) {
// Select the server corresponding to the longest matching prefix. Note that it is enough to
// pick the first match because the entries have already been sorted by descending prefix
// length when the connection was created.
for _, entry := range c.urlTable {
if entry.re.MatchString(request.URL.Path) {
base = entry.url
return
}
}
if base == nil {
err = fmt.Errorf(
"can't find any matching URL for request path '%s'",
request.URL.Path,
)
}
return
}