package gitea import ( "context" "errors" "io" "mime/multipart" "net/http" "net/url" "strconv" ) func (c Client) uploadAsset(ctx context.Context, client *http.Client, token, apiRoot string, releaseID int64, filename string, content io.Reader) (int, error) { if content == nil { return 0, failure(FailureInvalid, "upload release asset", 0, errors.New("asset content reader is required")) } uploadURL := apiRoot + "/releases/" + strconv.FormatInt(releaseID, 10) + "/assets" parsed, err := url.Parse(uploadURL) if err != nil { return 0, failure(FailureInvalid, "prepare asset upload", 0, err) } query := parsed.Query() query.Set("name", filename) parsed.RawQuery = query.Encode() reader, writer := io.Pipe() multipartWriter := multipart.NewWriter(writer) writeDone := make(chan error, 1) go func() { part, partErr := multipartWriter.CreateFormFile("attachment", filename) if partErr == nil { _, partErr = io.Copy(part, content) } closeErr := multipartWriter.Close() if partErr == nil { partErr = closeErr } _ = writer.CloseWithError(partErr) writeDone <- partErr }() request, err := http.NewRequestWithContext(ctx, http.MethodPost, parsed.String(), reader) if err != nil { _ = reader.CloseWithError(err) <-writeDone return 0, failure(FailureInvalid, "upload release asset", 0, err) } request.Header.Set("Authorization", "token "+token) request.Header.Set("Accept", "application/json") request.Header.Set("Content-Type", multipartWriter.FormDataContentType()) response, requestErr := client.Do(request) if requestErr != nil { _ = reader.CloseWithError(requestErr) <-writeDone return 0, failure(FailureTransport, "upload release asset", 0, requestErr) } defer response.Body.Close() writeErr := <-writeDone if response.StatusCode < 200 || response.StatusCode >= 300 { return response.StatusCode, responseFailure("upload release asset", response.StatusCode) } var uploaded assetResponse if err := decodeJSON(response.Body, &uploaded); err != nil { return response.StatusCode, failure(FailureMalformed, "decode uploaded asset", response.StatusCode, err) } if uploaded.ID <= 0 || uploaded.Name != filename { return response.StatusCode, failure(FailureMalformed, "verify uploaded asset", response.StatusCode, errors.New("upload response does not identify the selected asset")) } if writeErr != nil { return response.StatusCode, failure(FailureMalformed, "stream uploaded asset", response.StatusCode, writeErr) } return response.StatusCode, nil }