Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
try to fix coordinates
Browse files Browse the repository at this point in the history
  • Loading branch information
blankdots committed Dec 14, 2023
1 parent a01afe7 commit a8f53aa
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 359 deletions.
139 changes: 70 additions & 69 deletions api/sda/sda.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"time"

"github.com/gin-gonic/gin"
"github.com/neicnordic/crypt4gh/model/headers"
"github.com/neicnordic/crypt4gh/streaming"
"github.com/neicnordic/sda-download/api/middleware"
"github.com/neicnordic/sda-download/internal/config"
Expand Down Expand Up @@ -176,15 +175,6 @@ func Download(c *gin.Context) {
return
}

// Get coordinates
coordinates, err := parseCoordinates(c.Request)
if err != nil {
log.Errorf("parsing of query param coordinates to crypt4gh format failed, reason: %v", err)
c.String(http.StatusBadRequest, err.Error())

return
}

c.Header("Content-Length", fmt.Sprint(fileDetails.DecryptedSize))
c.Header("Content-Type", "application/octet-stream")
if c.GetBool("S3") {
Expand All @@ -206,90 +196,101 @@ func Download(c *gin.Context) {
return
}

// Stitch file and prepare it for streaming
fileStream, err := stitchFile(fileDetails.Header, file, coordinates)
hr := bytes.NewReader(fileDetails.Header)
mr := io.MultiReader(hr, file)
c4ghr, err := streaming.NewCrypt4GHReader(mr, *config.Config.App.Crypt4GHKey, nil)
if err != nil {
log.Errorf("could not prepare file for streaming, %s", err)
c.String(http.StatusInternalServerError, "file stream error")

return
}
defer c4ghr.Close()

sendStream(c, c4ghr)

sendStream(c.Writer, fileStream)
}

// stitchFile stitches the header and file body together for Crypt4GHReader
// and returns a streamable Reader
var stitchFile = func(header []byte, file io.ReadCloser, coordinates *headers.DataEditListHeaderPacket) (*streaming.Crypt4GHReader, error) {
log.Debugf("stitching header to file %s for streaming", file)
// Stitch header and file body together
hr := bytes.NewReader(header)
mr := io.MultiReader(hr, file)
c4ghr, err := streaming.NewCrypt4GHReader(mr, *config.Config.App.Crypt4GHKey, coordinates)
// sendStream streams file contents from a reader
var sendStream = func(c *gin.Context, c4ghr *streaming.Crypt4GHReader) {
log.Debug("begin data stream")

// Get query params
qStart := c.DefaultQuery("startCoordinate", "0")
qEnd := c.DefaultQuery("endCoordinate", "0")

// Parse and verify coordinates are valid
start, err := strconv.ParseInt(qStart, 10, 0)
if err != nil {
log.Errorf("failed to create Crypt4GH stream reader, %v", err)
log.Errorf("failed to convert start coordinate %d to integer, %s", start, err)
c.String(http.StatusInternalServerError, "startCoordinate must be an integer")

return nil, err
return
}
log.Debugf("file stream for %s constructed", file)

return c4ghr, nil
}
end, err := strconv.ParseInt(qEnd, 10, 0)
if err != nil {
log.Errorf("failed to convert end coordinate %d to integer, %s", end, err)

// parseCoordinates takes query param coordinates and converts them to
// Crypt4GH reader format
var parseCoordinates = func(r *http.Request) (*headers.DataEditListHeaderPacket, error) {
c.String(http.StatusInternalServerError, "endCoordinate must be an integer")

coordinates := &headers.DataEditListHeaderPacket{}
return
}
if end < start {
log.Errorf("endCoordinate=%d must be greater than startCoordinate=%d", end, start)

// Get query params
qStart := r.URL.Query().Get("startCoordinate")
qEnd := r.URL.Query().Get("endCoordinate")
c.String(http.StatusInternalServerError, "endCoordinate must be greater than startCoordinate")

// Parse and verify coordinates are valid
if len(qStart) > 0 && len(qEnd) > 0 {
start, err := strconv.ParseUint(qStart, 10, 64)
if err != nil {
log.Errorf("failed to convert start coordinate %d to integer, %s", start, err)
return
}

return nil, errors.New("startCoordinate must be an integer")
}
end, err := strconv.ParseUint(qEnd, 10, 64)
if err != nil {
log.Errorf("failed to convert end coordinate %d to integer, %s", end, err)
if start != 0 {
// We don't want to read from start, skip ahead to where we should be
if _, err := c4ghr.Seek(start, 0); err != nil {

return nil, errors.New("endCoordinate must be an integer")
}
if end < start {
log.Errorf("endCoordinate=%d must be greater than startCoordinate=%d", end, start)
c.String(http.StatusInternalServerError, "endCoordinate must be greater than startCoordinate")

return nil, errors.New("endCoordinate must be greater than startCoordinate")
return
}
// API query params take a coordinate range to read "start...end"
// But Crypt4GHReader takes a start byte and number of bytes to read "start...(end-start)"
bytesToRead := end - start
coordinates.NumberLengths = 2
coordinates.Lengths = []uint64{start, bytesToRead}
} else {
coordinates = nil
}

return coordinates, nil
}
// Calculate how much we should read (if given)
togo := end - start

// sendStream streams file contents from a reader
var sendStream = func(w http.ResponseWriter, file io.Reader) {
log.Debug("begin data stream")
buf := make([]byte, 4096)

n, err := io.Copy(w, file)
log.Debug("end data stream")
// Loop until we've read what we should (if no/faulty end given, that's EOF)
for end == 0 || togo > 0 {
rbuf := buf

if err != nil {
log.Errorf("file streaming failed, reason: %v", err)
http.Error(w, "file streaming failed", 500)
if end != 0 && togo < 4096 {
// If we don't want to read as much as 4096 bytes
rbuf = buf[:togo]
}
r, err := c4ghr.Read(rbuf)
togo -= int64(r)

return
}
// Nothing more to read?
if err == io.EOF && r == 0 {
// Fall out without error if we had EOF (if we got any data, do one
// more lap in the loop)
return
}

log.Debugf("Sent %d bytes", n)
if err != nil && err != io.EOF {
// An error we want to signal?
return
}

wbuf := rbuf[:r]
for len(wbuf) > 0 {
// Loop until we've written all that we could read,
// fall out on error
w, err := c.Writer.Write(wbuf)

if err != nil {
return
}
wbuf = wbuf[w:]
}
}
}
Loading

0 comments on commit a8f53aa

Please sign in to comment.