transformResponse converts an API response into a structured API object
(resp *http.Response, req *http.Request)
| 835 | |
| 836 | // transformResponse converts an API response into a structured API object |
| 837 | func (r *Request) transformResponse(resp *http.Response, req *http.Request) Result { |
| 838 | var body []byte |
| 839 | if resp.Body != nil { |
| 840 | data, err := ioutil.ReadAll(resp.Body) |
| 841 | switch err.(type) { |
| 842 | case nil: |
| 843 | body = data |
| 844 | case http2.StreamError: |
| 845 | // This is trying to catch the scenario that the server may close the connection when sending the |
| 846 | // response body. This can be caused by server timeout due to a slow network connection. |
| 847 | // TODO: Add test for this. Steps may be: |
| 848 | // 1. client-go (or kubectl) sends a GET request. |
| 849 | // 2. Apiserver sends back the headers and then part of the body |
| 850 | // 3. Apiserver closes connection. |
| 851 | // 4. client-go should catch this and return an error. |
| 852 | klog.V(2).Infof("Stream error %#v when reading response body, may be caused by closed connection.", err) |
| 853 | streamErr := fmt.Errorf("Stream error when reading response body, may be caused by closed connection. Please retry. Original error: %v", err) |
| 854 | return Result{ |
| 855 | err: streamErr, |
| 856 | } |
| 857 | default: |
| 858 | klog.Errorf("Unexpected error when reading response body: %v", err) |
| 859 | unexpectedErr := fmt.Errorf("Unexpected error when reading response body. Please retry. Original error: %v", err) |
| 860 | return Result{ |
| 861 | err: unexpectedErr, |
| 862 | } |
| 863 | } |
| 864 | } |
| 865 | |
| 866 | glogBody("Response Body", body) |
| 867 | |
| 868 | // verify the content type is accurate |
| 869 | contentType := resp.Header.Get("Content-Type") |
| 870 | decoder := r.serializers.Decoder |
| 871 | if len(contentType) > 0 && (decoder == nil || (len(r.content.ContentType) > 0 && contentType != r.content.ContentType)) { |
| 872 | mediaType, params, err := mime.ParseMediaType(contentType) |
| 873 | if err != nil { |
| 874 | return Result{err: errors.NewInternalError(err)} |
| 875 | } |
| 876 | decoder, err = r.serializers.RenegotiatedDecoder(mediaType, params) |
| 877 | if err != nil { |
| 878 | // if we fail to negotiate a decoder, treat this as an unstructured error |
| 879 | switch { |
| 880 | case resp.StatusCode == http.StatusSwitchingProtocols: |
| 881 | // no-op, we've been upgraded |
| 882 | case resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent: |
| 883 | return Result{err: r.transformUnstructuredResponseError(resp, req, body)} |
| 884 | } |
| 885 | return Result{ |
| 886 | body: body, |
| 887 | contentType: contentType, |
| 888 | statusCode: resp.StatusCode, |
| 889 | } |
| 890 | } |
| 891 | } |
| 892 | |
| 893 | switch { |
| 894 | case resp.StatusCode == http.StatusSwitchingProtocols: |