Add Content-Length and Content-Disposition Header
for GetFileContent route.
This commit is contained in:
@ -139,7 +139,7 @@ func (r *RunnerController) fileContent(writer http.ResponseWriter, request *http
|
||||
privilegedExecution = false
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "application/octet-stream")
|
||||
writer.Header().Set("Content-Disposition", "attachment; filename=\""+path+"\"")
|
||||
err = targetRunner.GetFileContent(path, writer, privilegedExecution, request.Context())
|
||||
if errors.Is(err, runner.ErrFileNotFound) {
|
||||
writeClientError(writer, err, http.StatusFailedDependency)
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/openHPI/poseidon/pkg/monitoring"
|
||||
"github.com/openHPI/poseidon/pkg/storage"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -125,7 +126,7 @@ func (w *AWSFunctionWorkload) UpdateFileSystem(request *dto.UpdateFileSystemRequ
|
||||
// GetFileContent is currently not supported with this aws serverless function.
|
||||
// This is because the function execution ends with the termination of the workload code.
|
||||
// So an on-demand file streaming after the termination is not possible. Also, we do not want to copy all files.
|
||||
func (w *AWSFunctionWorkload) GetFileContent(_ string, _ io.Writer, _ bool, _ context.Context) error {
|
||||
func (w *AWSFunctionWorkload) GetFileContent(_ string, _ http.ResponseWriter, _ bool, _ context.Context) error {
|
||||
return dto.ErrNotSupported
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/openHPI/poseidon/pkg/nullio"
|
||||
"github.com/openHPI/poseidon/pkg/storage"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -27,6 +28,9 @@ const (
|
||||
// executionTimeoutGracePeriod is the time to wait after sending a SIGQUIT signal to a timed out execution.
|
||||
// If the execution does not return after this grace period, the runner is destroyed.
|
||||
executionTimeoutGracePeriod = 3 * time.Second
|
||||
// lsCommand is our format for parsing information of a file(system).
|
||||
lsCommand = "ls -l --time-style=+%s -1 --literal"
|
||||
lsCommandRecursive = lsCommand + " --recursive"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -121,9 +125,9 @@ func (r *NomadJob) ExecuteInteractively(
|
||||
func (r *NomadJob) ListFileSystem(
|
||||
path string, recursive bool, content io.Writer, privilegedExecution bool, ctx context.Context) error {
|
||||
r.ResetTimeout()
|
||||
command := "ls -l --time-style=+%s -1 --literal"
|
||||
command := lsCommand
|
||||
if recursive {
|
||||
command += " --recursive"
|
||||
command = lsCommandRecursive
|
||||
}
|
||||
|
||||
ls2json := &nullio.Ls2JsonWriter{Target: content}
|
||||
@ -174,13 +178,17 @@ func (r *NomadJob) UpdateFileSystem(copyRequest *dto.UpdateFileSystemRequest) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *NomadJob) GetFileContent(path string, content io.Writer, privilegedExecution bool, ctx context.Context) error {
|
||||
func (r *NomadJob) GetFileContent(
|
||||
path string, content http.ResponseWriter, privilegedExecution bool, ctx context.Context) error {
|
||||
r.ResetTimeout()
|
||||
|
||||
retrieveCommand := (&dto.ExecutionRequest{Command: fmt.Sprintf("cat %q", path)}).FullCommand()
|
||||
contentLengthWriter := &nullio.ContentLengthWriter{Target: content}
|
||||
retrieveCommand := (&dto.ExecutionRequest{
|
||||
Command: fmt.Sprintf("%s %q && cat %q", lsCommand, path, path),
|
||||
}).FullCommand()
|
||||
// Improve: Instead of using io.Discard use a **fixed-sized** buffer. With that we could improve the error message.
|
||||
exitCode, err := r.api.ExecuteCommand(r.id, ctx, retrieveCommand, false, privilegedExecution,
|
||||
&nullio.Reader{}, content, io.Discard)
|
||||
&nullio.Reader{}, contentLengthWriter, io.Discard)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: nomad error during retrieve file content copy: %v",
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/openHPI/poseidon/internal/nomad"
|
||||
"github.com/openHPI/poseidon/pkg/dto"
|
||||
"github.com/openHPI/poseidon/pkg/logging"
|
||||
"github.com/openHPI/poseidon/pkg/nullio"
|
||||
"github.com/openHPI/poseidon/pkg/storage"
|
||||
"github.com/openHPI/poseidon/tests"
|
||||
@ -403,6 +404,6 @@ func NewRunner(id string, manager Accessor) Runner {
|
||||
func (s *UpdateFileSystemTestSuite) TestGetFileContentReturnsErrorIfExitCodeIsNotZero() {
|
||||
s.mockedExecuteCommandCall.RunFn = nil
|
||||
s.mockedExecuteCommandCall.Return(1, nil)
|
||||
err := s.runner.GetFileContent("", &bytes.Buffer{}, false, context.Background())
|
||||
err := s.runner.GetFileContent("", logging.NewLoggingResponseWriter(nil), false, context.Background())
|
||||
s.ErrorIs(err, ErrFileNotFound)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/openHPI/poseidon/pkg/monitoring"
|
||||
"github.com/openHPI/poseidon/pkg/storage"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ExitInfo struct {
|
||||
@ -54,7 +55,7 @@ type Runner interface {
|
||||
|
||||
// GetFileContent streams the file content at the requested path into the Writer provided at content.
|
||||
// The result is streamed via the io.Writer in order to not overload the memory with user input.
|
||||
GetFileContent(path string, content io.Writer, privilegedExecution bool, ctx context.Context) error
|
||||
GetFileContent(path string, content http.ResponseWriter, privilegedExecution bool, ctx context.Context) error
|
||||
|
||||
// Destroy destroys the Runner in Nomad.
|
||||
Destroy() error
|
||||
|
@ -4,10 +4,12 @@ package runner
|
||||
|
||||
import (
|
||||
context "context"
|
||||
io "io"
|
||||
http "net/http"
|
||||
|
||||
dto "github.com/openHPI/poseidon/pkg/dto"
|
||||
|
||||
io "io"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
time "time"
|
||||
@ -93,11 +95,11 @@ func (_m *RunnerMock) ExecutionExists(id string) bool {
|
||||
}
|
||||
|
||||
// GetFileContent provides a mock function with given fields: path, content, privilegedExecution, ctx
|
||||
func (_m *RunnerMock) GetFileContent(path string, content io.Writer, privilegedExecution bool, ctx context.Context) error {
|
||||
func (_m *RunnerMock) GetFileContent(path string, content http.ResponseWriter, privilegedExecution bool, ctx context.Context) error {
|
||||
ret := _m.Called(path, content, privilegedExecution, ctx)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, io.Writer, bool, context.Context) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, http.ResponseWriter, bool, context.Context) error); ok {
|
||||
r0 = rf(path, content, privilegedExecution, ctx)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
|
Reference in New Issue
Block a user