Skip to content

Commit 10ddf50

Browse files
committed
fix: prevent OOM on large upload/download
- Refactored Upload function to stream file content instead of loading it all into memory (fixes OOM issue for large files) - Added log.IsVerbose() to expose verbose state from log package - Updated Download function to dump HTTP response only when verbose mode is enabled, to avoid potential OOM on large responses Changelog: None Ticket: None Signed-off-by: Quentin Dufournet <[email protected]>
1 parent 97f633c commit 10ddf50

File tree

3 files changed

+48
-24
lines changed

3 files changed

+48
-24
lines changed

client/deviceconnect/client.go

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
package deviceconnect
1515

1616
import (
17-
"bytes"
1817
"context"
1918
"crypto/tls"
2019
"encoding/json"
@@ -26,6 +25,7 @@ import (
2625
"net/http/httputil"
2726
"net/url"
2827
"os"
28+
"path/filepath"
2929
"strconv"
3030
"strings"
3131
"sync"
@@ -257,36 +257,53 @@ func NewDeviceConnectError(errCode int, r io.Reader) *DeviceConnectError {
257257
}
258258

259259
func (c *Client) Upload(sourcePath string, deviceSpec *DeviceSpec) error {
260-
body := &bytes.Buffer{}
261-
writer := multipart.NewWriter(body)
262260
file, err := os.Open(sourcePath)
263261
if err != nil {
264262
return err
265263
}
266-
fi, err := os.Stat(sourcePath)
267-
if err != nil {
268-
return err
269-
}
270-
log.Verbf("Uploading the file to %s\n", deviceSpec.DevicePath)
271-
if err = writer.WriteField("path", deviceSpec.DevicePath); err != nil {
272-
return err
273-
}
274-
part, err := writer.CreateFormFile("file", sourcePath)
264+
defer file.Close()
265+
266+
fi, err := file.Stat()
275267
if err != nil {
276268
return err
277269
}
278-
if _, err = io.Copy(part, file); err != nil {
279-
return err
280-
}
281-
if err = writer.WriteField("mode", fmt.Sprintf("%o", fi.Mode())); err != nil {
282-
return err
283-
}
284-
if err = writer.Close(); err != nil {
285-
return err
286-
}
270+
271+
pr, pw := io.Pipe()
272+
writer := multipart.NewWriter(pw)
273+
274+
go func() {
275+
defer pw.Close()
276+
277+
if err := writer.WriteField("path", deviceSpec.DevicePath); err != nil {
278+
pw.CloseWithError(err)
279+
return
280+
}
281+
282+
part, err := writer.CreateFormFile("file", filepath.Base(sourcePath))
283+
if err != nil {
284+
pw.CloseWithError(err)
285+
return
286+
}
287+
288+
if _, err := io.Copy(part, file); err != nil {
289+
pw.CloseWithError(err)
290+
return
291+
}
292+
293+
if err := writer.WriteField("mode", fmt.Sprintf("%o", fi.Mode())); err != nil {
294+
pw.CloseWithError(err)
295+
return
296+
}
297+
298+
if err := writer.Close(); err != nil {
299+
pw.CloseWithError(err)
300+
return
301+
}
302+
}()
303+
287304
req, err := http.NewRequest(http.MethodPut,
288305
c.url+fileUploadURL+"devices/"+deviceSpec.DeviceID+"/upload",
289-
body)
306+
pr)
290307
if err != nil {
291308
return err
292309
}
@@ -344,8 +361,10 @@ func (c *Client) Download(deviceSpec *DeviceSpec, sourcePath string) error {
344361
}
345362
defer resp.Body.Close()
346363

347-
rspDump, _ := httputil.DumpResponse(resp, true)
348-
log.Verbf("Response: \n%v\n", string(rspDump))
364+
if log.IsVerbose() {
365+
rspDump, _ := httputil.DumpResponse(resp, true)
366+
log.Verbf("Response: \n%v\n", string(rspDump))
367+
}
349368

350369
switch resp.StatusCode {
351370
case http.StatusOK:

licenses.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ github.com/minio/sha256-simd,Apache-2.0
1717
github.com/mitchellh/mapstructure,MIT
1818
github.com/pelletier/go-toml/v2,MIT
1919
github.com/pkg/errors,BSD-2-Clause
20+
github.com/remyoudompheng/go-liblzma,BSD-3-Clause
2021
github.com/rivo/uniseg,MIT
2122
github.com/sagikazarmark/slog-shim,BSD-3-Clause
2223
github.com/spf13/afero,Apache-2.0

log/log.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ func Setup(verb bool) {
2626
verbose = verb
2727
}
2828

29+
func IsVerbose() bool {
30+
return verbose
31+
}
32+
2933
func Err(msg string) {
3034
fmt.Fprintln(os.Stderr, msg)
3135
}

0 commit comments

Comments
 (0)