NewUploadRequest creates an upload request. A relative URL can be provided in urlStr, in which case it is resolved relative to the UploadURL of the Client. Relative URLs should always be specified without a preceding slash.
(ctx context.Context, urlStr string, reader io.Reader, size int64, mediaType string, opts ...RequestOption)
| 883 | // urlStr, in which case it is resolved relative to the UploadURL of the Client. |
| 884 | // Relative URLs should always be specified without a preceding slash. |
| 885 | func (c *Client) NewUploadRequest(ctx context.Context, urlStr string, reader io.Reader, size int64, mediaType string, opts ...RequestOption) (*http.Request, error) { |
| 886 | if !strings.HasSuffix(c.uploadURL.Path, "/") { |
| 887 | return nil, fmt.Errorf("uploadURL must have a trailing slash, but %q does not", c.uploadURL) |
| 888 | } |
| 889 | |
| 890 | if err := checkURLPathTraversal(urlStr); err != nil { |
| 891 | return nil, err |
| 892 | } |
| 893 | |
| 894 | u, err := c.uploadURL.Parse(urlStr) |
| 895 | if err != nil { |
| 896 | return nil, err |
| 897 | } |
| 898 | |
| 899 | requestBody := reader |
| 900 | if reader != nil { |
| 901 | // Wrap the provided reader so transport code does not observe concrete body types |
| 902 | // (for example *os.File) and switch to platform-specific sendfile fast paths. |
| 903 | // |
| 904 | // Why this exists: |
| 905 | // race-enabled test runs on Windows have surfaced data races in the sendfile path |
| 906 | // while request read/write loops run concurrently. Hiding concrete type information |
| 907 | // keeps uploads on the generic io.Reader copy path, which is race-stable and preserves |
| 908 | // request semantics (same bytes, same headers, same content length). |
| 909 | requestBody = uploadRequestBodyReader{Reader: reader} |
| 910 | } |
| 911 | |
| 912 | req, err := http.NewRequestWithContext(ctx, "POST", u.String(), requestBody) |
| 913 | if err != nil { |
| 914 | return nil, err |
| 915 | } |
| 916 | |
| 917 | req.ContentLength = size |
| 918 | |
| 919 | if mediaType == "" { |
| 920 | mediaType = defaultMediaType |
| 921 | } |
| 922 | req.Header.Set("Content-Type", mediaType) |
| 923 | req.Header.Set("Accept", mediaTypeV3) |
| 924 | req.Header.Set("User-Agent", c.userAgent) |
| 925 | req.Header.Set(headerAPIVersion, defaultAPIVersion) |
| 926 | |
| 927 | for _, opt := range opts { |
| 928 | opt(req) |
| 929 | } |
| 930 | |
| 931 | return req, nil |
| 932 | } |
| 933 | |
| 934 | // uploadRequestBodyReader intentionally wraps an io.Reader to hide concrete reader types. |
| 935 | // See NewUploadRequest for why this prevents race-prone transport optimizations. |