diff --git a/api/sda/sda.go b/api/sda/sda.go index ef22f21..2a51c8f 100644 --- a/api/sda/sda.go +++ b/api/sda/sda.go @@ -156,6 +156,7 @@ func Download(c *gin.Context) { c.String(http.StatusUnauthorized, "unauthorised") return + } // Get file header @@ -166,6 +167,43 @@ func Download(c *gin.Context) { return } + // 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 convert start coordinate %d to integer, %s", start, err) + c.String(http.StatusBadRequest, "startCoordinate must be an integer") + + return + } + end, err := strconv.ParseInt(qEnd, 10, 0) + if err != nil { + log.Errorf("failed to convert end coordinate %d to integer, %s", end, err) + + c.String(http.StatusBadRequest, "endCoordinate must be an integer") + + return + } + if end < start { + log.Errorf("endCoordinate=%d must be greater than startCoordinate=%d", end, start) + + c.String(http.StatusBadRequest, "endCoordinate must be greater than startCoordinate") + + return + } + + if start == 0 && end == 0 { + c.Header("Content-Length", fmt.Sprint(fileDetails.DecryptedSize)) + } else { + // Calculate how much we should read (if given) + togo := end - start + c.Header("Content-Length", fmt.Sprint(togo)) + } + // Get archive file handle file, err := Backend.NewFileReader(fileDetails.ArchivePath) if err != nil { @@ -195,65 +233,41 @@ func Download(c *gin.Context) { return } - // Stitch file and prepare it for streaming - var fileStream *streaming.Crypt4GHReader + // Prepare the file for streaming, encrypted or decrypted + var encryptedFileReader io.Reader + var fileStream io.Reader + hr := bytes.NewReader(fileDetails.Header) + encryptedFileReader = io.MultiReader(hr, file) + switch c.Param("type") { case "encrypted": - log.Print("Return encrypted file") - fileStream, err = stitchEncryptedFile(fileDetails.Header, file) - if err != nil { - log.Errorf("could not prepare file for streaming, %s", err) - c.String(http.StatusInternalServerError, "file stream error") + if start > 0 || end > 0 { + // unset content-length + c.Header("Content-Length", "-1") + log.Errorf("Start and end coordinates for encrypted files not implemented! %v", start) + c.String(http.StatusInternalServerError, "an error occurred") return } - c.Header("Content-Length", "") + fileStream = encryptedFileReader + default: - // Stitch file and prepare it for streaming - fileStream, err = stitchFile(fileDetails.Header, file) + c4ghfileStream, err := streaming.NewCrypt4GHReader(encryptedFileReader, *config.Config.App.Crypt4GHKey, nil) + defer c4ghfileStream.Close() if err != nil { log.Errorf("could not prepare file for streaming, %s", err) c.String(http.StatusInternalServerError, "file stream error") return } - } - - // 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 convert start coordinate %d to integer, %s", start, err) - c.String(http.StatusBadRequest, "startCoordinate must be an integer") - - return - } - end, err := strconv.ParseInt(qEnd, 10, 0) - if err != nil { - log.Errorf("failed to convert end coordinate %d to integer, %s", end, err) - - c.String(http.StatusBadRequest, "endCoordinate must be an integer") - - return - } - if end < start { - log.Errorf("endCoordinate=%d must be greater than startCoordinate=%d", end, start) - - c.String(http.StatusBadRequest, "endCoordinate must be greater than startCoordinate") - - return - } + err = truncateStream(c4ghfileStream, c.Writer, start) + fileStream = c4ghfileStream + if err != nil { + log.Errorf("error occurred while finding sending start: %v", err) + c.String(http.StatusInternalServerError, "an error occurred") - if start == 0 && end == 0 { - c.Header("Content-Length", fmt.Sprint(fileDetails.DecryptedSize)) - } else { - // Calculate how much we should read (if given) - togo := end - start - c.Header("Content-Length", fmt.Sprint(togo)) + return + } } err = sendStream(fileStream, c.Writer, start, end) @@ -265,39 +279,7 @@ func Download(c *gin.Context) { } } -// stitchFile stitches the header and file body together for Crypt4GHReader -// and returns a streamable Reader -var stitchFile = func(header []byte, file io.ReadCloser) (*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, nil) - //defer c4ghr.Close() - return c4ghr, err -} - -// stitchEncryptedFile stitches the header and file body together for Crypt4GHReader -// and returns a streamable Reader -var stitchEncryptedFile = func(header []byte, file io.ReadCloser) (*streaming.Crypt4GHReader, error) { - log.Debugf("stitching header to file %s for streaming", file) - // Stitch header and file body together - hr := bytes.NewReader(header) - - encryptedFile := io.MultiReader(hr, io.MultiReader(hr, file)) - - log.Print("Encrypted file:", encryptedFile) - - log.Debugf("file stream for %s constructed", file) - c4ghr, err := streaming.NewCrypt4GHReader(encryptedFile, *config.Config.App.Crypt4GHKey, nil) - - return c4ghr, err -} - -// sendStream -// used from: https://github.com/neicnordic/crypt4gh/blob/master/examples/reader/main.go#L48C1-L113C1 -var sendStream = func(reader *streaming.Crypt4GHReader, writer http.ResponseWriter, start, end int64) error { +var truncateStream = func(reader *streaming.Crypt4GHReader, writer http.ResponseWriter, start int64) error { if start != 0 { // We don't want to read from start, skip ahead to where we should be @@ -305,6 +287,11 @@ var sendStream = func(reader *streaming.Crypt4GHReader, writer http.ResponseWrit return err } } + return nil +} + +// used from: https://github.com/neicnordic/crypt4gh/blob/master/examples/reader/main.go#L48C1-L113C1 +var sendStream = func(reader io.Reader, writer http.ResponseWriter, start, end int64) error { // Calculate how much we should read (if given) togo := end - start