rolling back to golangci-lint 1.20.1 + moving normize filename to resource & tests with a file with special chars in its name + webhookurl custom headers done + preparing remote url custom headers

This commit is contained in:
Julien Neuhart
2019-12-05 18:16:02 +01:00
parent 5140e4ec9a
commit 782f6ac27e
10 changed files with 191 additions and 26 deletions

View File

@@ -308,20 +308,47 @@ func convertAsync(ctx context.Context, p printer.Printer, filename, fpath string
defer f.Close() // nolint: errcheck
logger.DebugfOp(
op,
"sending result file '%s' to '%s'",
"preparing to send result file '%s' to '%s'...",
filename,
webhookURL,
)
httpClient := &http.Client{
Timeout: xtime.Duration(webhookURLTimeout),
}
resp, err := httpClient.Post(webhookURL, "application/pdf", f) /* #nosec */
req, err := http.NewRequest(http.MethodPost, webhookURL, f)
if err != nil {
xerr := xerror.New(op, err)
logger.ErrorOp(xerror.Op(xerr), xerr)
return
}
req.Header.Set(echo.HeaderContentType, "application/pdf")
// set custom headers (if any).
for key, value := range resource.WebhookURLCustomHeaders(r) {
for _, v := range value {
req.Header.Add(key, v)
logger.DebugfOp(op, "added '%s' to custom header '%s'", v, key)
}
}
// send the result file.
logger.DebugfOp(
op,
"sending result file '%s' to '%s'...",
filename,
webhookURL,
)
resp, err := httpClient.Do(req) /* #nosec */
if err != nil {
xerr := xerror.New(op, err)
logger.ErrorOp(xerror.Op(xerr), xerr)
return
}
defer resp.Body.Close() // nolint: errcheck
logger.DebugfOp(
op,
"result file '%s' sent to '%s'",
filename,
webhookURL,
)
}()
return nil
}

View File

@@ -579,11 +579,18 @@ func TestOfficeHandler(t *testing.T) {
}
func TestWebhook(t *testing.T) {
customHeaderRealKey := http.CanonicalHeaderKey("MyCustomHeader")
customHeaderKey := fmt.Sprintf("%s%s", resource.WebhookURLCustomHeaderCanonicalBaseKey, customHeaderRealKey)
customHeaderValue := "foo"
status := make(chan error, 2)
rcv := echo.New()
rcv.POST("/foo", func(c echo.Context) error {
if c.Request().Header.Get("Content-type") != "application/pdf" {
status <- fmt.Errorf("wrong Content-type: got %s want %s", c.Request().Header.Get("Content-type"), "application/pdf")
if c.Request().Header.Get(echo.HeaderContentType) != "application/pdf" {
status <- fmt.Errorf("wrong Content-type: got '%s' want '%s'", c.Request().Header.Get(echo.HeaderContentType), "application/pdf")
return nil
}
if c.Request().Header.Get(customHeaderRealKey) != customHeaderValue {
status <- fmt.Errorf("wrong '%s': got '%s' want '%s'", customHeaderRealKey, c.Request().Header.Get(customHeaderRealKey), customHeaderValue)
return nil
}
body, err := ioutil.ReadAll(c.Request().Body)
@@ -607,6 +614,7 @@ func TestWebhook(t *testing.T) {
body, contentType := test.MergeMultipartForm(t, map[string]string{string(resource.WebhookURLArgKey): "http://localhost:3001/foo"})
req := httptest.NewRequest(http.MethodPost, mergeEndpoint, body)
req.Header.Set(echo.HeaderContentType, contentType)
req.Header.Set(customHeaderKey, customHeaderValue)
test.AssertStatusCode(t, http.StatusOK, srv, req)
err := <-status
assert.NoError(t, err)
@@ -620,5 +628,5 @@ func TestResultFilename(t *testing.T) {
req.Header.Set(echo.HeaderContentType, contentType)
rec := httptest.NewRecorder()
srv.ServeHTTP(rec, req)
assert.Equal(t, "attachment; filename=\"foo.pdf\"", rec.Header().Get("Content-Disposition"))
assert.Equal(t, "attachment; filename=\"foo.pdf\"", rec.Header().Get(echo.HeaderContentDisposition))
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/labstack/echo/v4"
"github.com/thecodingmachine/gotenberg/internal/app/xhttp/pkg/resource"
"github.com/thecodingmachine/gotenberg/internal/pkg/conf"
"github.com/thecodingmachine/gotenberg/internal/pkg/normalize"
"github.com/thecodingmachine/gotenberg/internal/pkg/xerror"
"github.com/thecodingmachine/gotenberg/internal/pkg/xlog"
)
@@ -79,6 +78,10 @@ func (ctx *Context) WithResource(directoryName string) error {
if err != nil {
return r, err
}
// retrieve custom headers from request.
for name, value := range ctx.Request().Header {
r.WithCustomHeader(name, value)
}
// retrieve form values from request.
for _, key := range resource.ArgKeys() {
r.WithArg(key, ctx.FormValue(string(key)))
@@ -103,11 +106,7 @@ func (ctx *Context) WithResource(directoryName string) error {
return r, err
}
defer in.Close() // nolint: errcheck
filename, err := normalize.String(fh.Filename)
if err != nil {
return r, err
}
if err := r.WithFile(filename, in); err != nil {
if err := r.WithFile(fh.Filename, in); err != nil {
return r, err
}
}

View File

@@ -0,0 +1,37 @@
package resource
import (
"strings"
)
const (
// RemoteURLCustomHeaderCanonicalBaseKey is the base key
// of custom headers send to the remote URL.
RemoteURLCustomHeaderCanonicalBaseKey string = "Gotenberg-Remoteurl-"
// WebhookURLCustomHeaderCanonicalBaseKey is the base key
// of custom headers send to the webhook URL.
WebhookURLCustomHeaderCanonicalBaseKey string = "Gotenberg-Webhookurl-"
)
func fetchCustomHeaders(r Resource, baseKey string) map[string][]string {
customHeaders := make(map[string][]string)
for key, value := range r.customHeaders {
if strings.Contains(key, baseKey) {
realKey := strings.Replace(key, baseKey, "", 1)
customHeaders[realKey] = value
}
}
return customHeaders
}
// RemoteURLCustomHeaders is a helper for retrieving
// the custom headers for the URL conversion.
func RemoteURLCustomHeaders(r Resource) map[string][]string {
return fetchCustomHeaders(r, RemoteURLCustomHeaderCanonicalBaseKey)
}
// WebhookURLCustomHeaders is a helper for retrieving
// the custom headers for the webhook URL.
func WebhookURLCustomHeaders(r Resource) map[string][]string {
return fetchCustomHeaders(r, WebhookURLCustomHeaderCanonicalBaseKey)
}

View File

@@ -0,0 +1,62 @@
package resource
import (
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/thecodingmachine/gotenberg/test"
)
func TestRemoteURLCustomHeaders(t *testing.T) {
const resourceDirectoryName string = "foo"
logger := test.DebugLogger()
r, err := New(logger, resourceDirectoryName)
assert.Nil(t, err)
// should find the custom header.
customHeaderValue := "bar"
customHeaderCanonicalRealKey := "Foo"
customHeaderCanonicalKey := http.CanonicalHeaderKey(fmt.Sprintf("%s%s", RemoteURLCustomHeaderCanonicalBaseKey, customHeaderCanonicalRealKey))
r.WithCustomHeader(customHeaderCanonicalKey, []string{customHeaderValue})
r.WithCustomHeader("Bar", []string{"Bar"})
expected := map[string][]string{
customHeaderCanonicalRealKey: []string{
customHeaderValue,
},
}
notExpected := map[string][]string{
customHeaderCanonicalKey: []string{
customHeaderValue,
},
}
v := RemoteURLCustomHeaders(r)
assert.Equal(t, expected, v)
assert.NotEqual(t, notExpected, v)
}
func TestWebhookURLCustomHeaders(t *testing.T) {
const resourceDirectoryName string = "foo"
logger := test.DebugLogger()
r, err := New(logger, resourceDirectoryName)
assert.Nil(t, err)
// should find the custom header.
customHeaderValue := "bar"
customHeaderCanonicalRealKey := "Foo"
customHeaderCanonicalKey := http.CanonicalHeaderKey(fmt.Sprintf("%s%s", WebhookURLCustomHeaderCanonicalBaseKey, customHeaderCanonicalRealKey))
r.WithCustomHeader(customHeaderCanonicalKey, []string{customHeaderValue})
r.WithCustomHeader("Bar", []string{"Bar"})
expected := map[string][]string{
customHeaderCanonicalRealKey: []string{
customHeaderValue,
},
}
notExpected := map[string][]string{
customHeaderCanonicalKey: []string{
customHeaderValue,
},
}
v := WebhookURLCustomHeaders(r)
assert.Equal(t, expected, v)
assert.NotEqual(t, notExpected, v)
}

View File

@@ -5,7 +5,9 @@ import (
"io"
"os"
"path/filepath"
"strings"
"github.com/thecodingmachine/gotenberg/internal/pkg/normalize"
"github.com/thecodingmachine/gotenberg/internal/pkg/xassert"
"github.com/thecodingmachine/gotenberg/internal/pkg/xerror"
"github.com/thecodingmachine/gotenberg/internal/pkg/xlog"
@@ -21,10 +23,11 @@ const TemporaryDirectory string = "tmp"
// Resource helps managing
// arguments and files for a conversion.
type Resource struct {
logger xlog.Logger
dirPath string
args map[ArgKey]string
files map[string]file
logger xlog.Logger
dirPath string
customHeaders map[string][]string
args map[ArgKey]string
files map[string]file
}
// New creates a Resource where its files will
@@ -48,10 +51,11 @@ func New(logger xlog.Logger, directoryName string) (Resource, error) {
}
logger.DebugfOp(op, "resource directory '%s' created", directoryName)
return Resource{
logger: logger,
dirPath: dirPath,
args: make(map[ArgKey]string),
files: make(map[string]file),
logger: logger,
dirPath: dirPath,
customHeaders: make(map[string][]string),
args: make(map[ArgKey]string),
files: make(map[string]file),
}, nil
}
@@ -70,6 +74,19 @@ func (r Resource) Close() error {
return nil
}
// WithCustomHeader add a new custom header to the Resource.
// Given key should be in canonical format.
func (r *Resource) WithCustomHeader(key string, value []string) {
const op string = "resource.Resource.WithCustomHeader"
if strings.Contains(key, RemoteURLCustomHeaderCanonicalBaseKey) ||
strings.Contains(key, WebhookURLCustomHeaderCanonicalBaseKey) {
r.customHeaders[key] = value
r.logger.DebugfOp(op, "added '%s' with value '%s' to resource custom headers", key, value)
return
}
r.logger.DebugfOp(op, "skipping '%s' as it is not a custom header...", key)
}
// WithArg add a new argument to the Resource.
func (r *Resource) WithArg(key ArgKey, value string) {
const op string = "resource.Resource.WithArg"
@@ -80,13 +97,24 @@ func (r *Resource) WithArg(key ArgKey, value string) {
// WithFile add a new file to the Resource.
func (r *Resource) WithFile(filename string, in io.Reader) error {
const op string = "resource.Resource.WithFile"
fpath := fmt.Sprintf("%s/%s", r.dirPath, filename)
file := file{fpath: fpath}
if err := file.write(in); err != nil {
resolver := func() error {
// see https://github.com/thecodingmachine/gotenberg/issues/104.
normalized, err := normalize.String(filename)
if err != nil {
return err
}
fpath := fmt.Sprintf("%s/%s", r.dirPath, normalized)
file := file{fpath: fpath}
if err := file.write(in); err != nil {
return err
}
r.files[filename] = file
r.logger.DebugfOp(op, "resource file '%s' created", filename)
return nil
}
if err := resolver(); err != nil {
return xerror.New(op, err)
}
r.files[filename] = file
r.logger.DebugfOp(op, "resource file '%s' created", filename)
return nil
}