github.com/openshift-online/ocm-sdk-go@v0.1.473/send.go (about) 1 /* 2 Copyright (c) 2018 Red Hat, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // This file contains the implementation of the methods of the connection that are used to send HTTP 18 // requests and receive HTTP responses. 19 20 package sdk 21 22 import ( 23 "context" 24 "fmt" 25 "net/http" 26 "path" 27 28 "github.com/openshift-online/ocm-sdk-go/internal" 29 ) 30 31 // RoundTrip is the implementation of the http.RoundTripper interface. 32 func (c *Connection) RoundTrip(request *http.Request) (response *http.Response, err error) { 33 // Check if the connection is closed: 34 err = c.checkClosed() 35 if err != nil { 36 return 37 } 38 39 // Get the context from the request: 40 ctx := request.Context() 41 42 // Check the request URL: 43 if request.URL.Path == "" { 44 err = fmt.Errorf("request path is mandatory") 45 return 46 } 47 if request.URL.Scheme != "" || request.URL.Host != "" || !path.IsAbs(request.URL.Path) { 48 err = fmt.Errorf("request URL '%s' isn't absolute", request.URL) 49 return 50 } 51 52 // Select the target server add the base URL to the request URL: 53 server, err := c.selectServer(ctx, request) 54 if err != nil { 55 return 56 } 57 request.URL = server.URL.ResolveReference(request.URL) 58 59 // Check the request method and body: 60 switch request.Method { 61 case http.MethodGet, http.MethodDelete: 62 if request.Body != nil { 63 c.logger.Warn(ctx, 64 "Request body is not allowed for the '%s' method", 65 request.Method, 66 ) 67 } 68 case http.MethodPost, http.MethodPatch, http.MethodPut: 69 // POST and PATCH and PUT don't need to have a body. It is up to the server to decide if 70 // this is acceptable. 71 default: 72 err = fmt.Errorf("method '%s' is not allowed", request.Method) 73 return 74 } 75 76 // Add the default headers: 77 if request.Header == nil { 78 request.Header = make(http.Header) 79 } 80 if c.agent != "" { 81 request.Header.Set("User-Agent", c.agent) 82 } 83 switch request.Method { 84 case http.MethodPost, http.MethodPatch, http.MethodPut: 85 request.Header.Set("Content-Type", "application/json") 86 } 87 request.Header.Set("Accept", "application/json") 88 89 // Select the client: 90 client, err := c.clientSelector.Select(ctx, server) 91 if err != nil { 92 return 93 } 94 95 // Send the request: 96 response, err = client.Do(request) 97 if err != nil { 98 return 99 } 100 101 // Check that the response content type is JSON: 102 err = internal.CheckContentType(response) 103 if err != nil { 104 return 105 } 106 107 return 108 } 109 110 // selectServer selects the server that should be used for the given request, according its path and 111 // the alternative URLs configured when the connection was created. 112 func (c *Connection) selectServer(ctx context.Context, 113 request *http.Request) (base *internal.ServerAddress, err error) { 114 // Select the server corresponding to the longest matching prefix. Note that it is enough to 115 // pick the first match because the entries have already been sorted by descending prefix 116 // length when the connection was created. 117 for _, entry := range c.urlTable { 118 if entry.re.MatchString(request.URL.Path) { 119 base = entry.url 120 return 121 } 122 } 123 if base == nil { 124 err = fmt.Errorf( 125 "can't find any matching URL for request path '%s'", 126 request.URL.Path, 127 ) 128 } 129 return 130 }