From 5140e4ec9a792fce428ed2767205c7c03b64865e Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Thu, 5 Dec 2019 15:43:24 +0100 Subject: [PATCH 1/7] updating golangci-lint --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ece45530..876e90db 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ VERSION=snapshot DOCKER_USER= DOCKER_PASSWORD= DOCKER_REPOSITORY=thecodingmachine -GOLANGCI_LINT_VERSION=1.19.1 +GOLANGCI_LINT_VERSION=1.21.0 CODE_COVERAGE=0 TINI_VERSION=0.18.0 MAXIMUM_WAIT_TIMEOUT=30.0 From 782f6ac27e8a13196b9b3b7513fac20ef80c8772 Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Thu, 5 Dec 2019 18:16:02 +0100 Subject: [PATCH 2/7] 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 --- Makefile | 2 +- build/lint/Dockerfile | 2 +- internal/app/xhttp/handler.go | 31 +++++++++- internal/app/xhttp/handler_test.go | 14 ++++- internal/app/xhttp/pkg/context/context.go | 11 ++-- internal/app/xhttp/pkg/resource/header.go | 37 +++++++++++ .../app/xhttp/pkg/resource/header_test.go | 62 +++++++++++++++++++ internal/app/xhttp/pkg/resource/resource.go | 54 ++++++++++++---- test/testdata.go | 1 + .../office/document_with_special_éà.txt | 3 + 10 files changed, 191 insertions(+), 26 deletions(-) create mode 100644 internal/app/xhttp/pkg/resource/header.go create mode 100644 internal/app/xhttp/pkg/resource/header_test.go create mode 100644 test/testdata/office/document_with_special_éà.txt diff --git a/Makefile b/Makefile index 876e90db..f3d59a02 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ VERSION=snapshot DOCKER_USER= DOCKER_PASSWORD= DOCKER_REPOSITORY=thecodingmachine -GOLANGCI_LINT_VERSION=1.21.0 +GOLANGCI_LINT_VERSION=1.20.1 CODE_COVERAGE=0 TINI_VERSION=0.18.0 MAXIMUM_WAIT_TIMEOUT=30.0 diff --git a/build/lint/Dockerfile b/build/lint/Dockerfile index fd08c9c5..42a8c054 100644 --- a/build/lint/Dockerfile +++ b/build/lint/Dockerfile @@ -33,4 +33,4 @@ RUN go mod download &&\ # Copy our code source. COPY --chown=gotenberg:gotenberg . . -CMD ["golangci-lint", "run" ,"--tests=false", "--enable-all", "--disable=dupl", "--disable=funlen" ] \ No newline at end of file +CMD ["golangci-lint", "run" ,"--tests=false", "--enable-all", "--disable=dupl", "--disable=funlen", "--disable=wsl", "--disable=gocognit" ] \ No newline at end of file diff --git a/internal/app/xhttp/handler.go b/internal/app/xhttp/handler.go index 7c819bd2..234c0903 100644 --- a/internal/app/xhttp/handler.go +++ b/internal/app/xhttp/handler.go @@ -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 } diff --git a/internal/app/xhttp/handler_test.go b/internal/app/xhttp/handler_test.go index 5602d90b..f3264567 100644 --- a/internal/app/xhttp/handler_test.go +++ b/internal/app/xhttp/handler_test.go @@ -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)) } diff --git a/internal/app/xhttp/pkg/context/context.go b/internal/app/xhttp/pkg/context/context.go index d1065b58..6a8122d1 100644 --- a/internal/app/xhttp/pkg/context/context.go +++ b/internal/app/xhttp/pkg/context/context.go @@ -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 } } diff --git a/internal/app/xhttp/pkg/resource/header.go b/internal/app/xhttp/pkg/resource/header.go new file mode 100644 index 00000000..ac284643 --- /dev/null +++ b/internal/app/xhttp/pkg/resource/header.go @@ -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) +} diff --git a/internal/app/xhttp/pkg/resource/header_test.go b/internal/app/xhttp/pkg/resource/header_test.go new file mode 100644 index 00000000..07c71f0a --- /dev/null +++ b/internal/app/xhttp/pkg/resource/header_test.go @@ -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) +} diff --git a/internal/app/xhttp/pkg/resource/resource.go b/internal/app/xhttp/pkg/resource/resource.go index 81cf001a..39d1229b 100644 --- a/internal/app/xhttp/pkg/resource/resource.go +++ b/internal/app/xhttp/pkg/resource/resource.go @@ -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 } diff --git a/test/testdata.go b/test/testdata.go index e40ab818..fc72472b 100644 --- a/test/testdata.go +++ b/test/testdata.go @@ -75,6 +75,7 @@ func OfficeFpaths(t *testing.T) []string { fpath(t, "office", "document.docx"), fpath(t, "office", "document.rtf"), fpath(t, "office", "document.txt"), + fpath(t, "office", "document_with_special_éà.txt"), } } diff --git a/test/testdata/office/document_with_special_éà.txt b/test/testdata/office/document_with_special_éà.txt new file mode 100644 index 00000000..16eab9d7 --- /dev/null +++ b/test/testdata/office/document_with_special_éà.txt @@ -0,0 +1,3 @@ +Gutenberg + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. \ No newline at end of file From 927f98c66ba3ed47ca3314c8b59acea06bdf8a66 Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Fri, 6 Dec 2019 11:22:11 +0100 Subject: [PATCH 3/7] adding custom headers for remoteURL --- internal/app/xhttp/handler.go | 12 ++++--- internal/app/xhttp/pkg/context/context.go | 4 +-- internal/app/xhttp/pkg/resource/header.go | 8 ++--- .../app/xhttp/pkg/resource/header_test.go | 32 +++++++----------- internal/app/xhttp/pkg/resource/resource.go | 19 ++++++----- internal/pkg/printer/chrome.go | 33 +++++++++++++++++++ test/multipartform.go | 2 +- 7 files changed, 71 insertions(+), 39 deletions(-) diff --git a/internal/app/xhttp/handler.go b/internal/app/xhttp/handler.go index 234c0903..03266198 100644 --- a/internal/app/xhttp/handler.go +++ b/internal/app/xhttp/handler.go @@ -124,6 +124,7 @@ func urlHandler(c echo.Context) error { if err != nil { return err } + opts.CustomHeaders = resource.RemoteURLCustomHeaders(r) if !r.HasArg(resource.RemoteURLArgKey) { return xerror.Invalid( op, @@ -323,11 +324,14 @@ func convertAsync(ctx context.Context, p printer.Printer, filename, fpath string } 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) + customHeaders := resource.WebhookURLCustomHeaders(r) + if len(customHeaders) > 0 { + for key, value := range customHeaders { + req.Header.Set(key, value) + logger.DebugfOp(op, "set '%s' to custom header '%s'", value, key) } + } else { + logger.DebugOp(op, "skipping custom headers as none have been provided...") } // send the result file. logger.DebugfOp( diff --git a/internal/app/xhttp/pkg/context/context.go b/internal/app/xhttp/pkg/context/context.go index 6a8122d1..41f430fb 100644 --- a/internal/app/xhttp/pkg/context/context.go +++ b/internal/app/xhttp/pkg/context/context.go @@ -79,8 +79,8 @@ func (ctx *Context) WithResource(directoryName string) error { return r, err } // retrieve custom headers from request. - for name, value := range ctx.Request().Header { - r.WithCustomHeader(name, value) + for key, value := range ctx.Request().Header { + r.WithCustomHeader(key, value[0]) } // retrieve form values from request. for _, key := range resource.ArgKeys() { diff --git a/internal/app/xhttp/pkg/resource/header.go b/internal/app/xhttp/pkg/resource/header.go index ac284643..9a9745d7 100644 --- a/internal/app/xhttp/pkg/resource/header.go +++ b/internal/app/xhttp/pkg/resource/header.go @@ -13,8 +13,8 @@ const ( WebhookURLCustomHeaderCanonicalBaseKey string = "Gotenberg-Webhookurl-" ) -func fetchCustomHeaders(r Resource, baseKey string) map[string][]string { - customHeaders := make(map[string][]string) +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) @@ -26,12 +26,12 @@ func fetchCustomHeaders(r Resource, baseKey string) map[string][]string { // RemoteURLCustomHeaders is a helper for retrieving // the custom headers for the URL conversion. -func RemoteURLCustomHeaders(r Resource) map[string][]string { +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 { +func WebhookURLCustomHeaders(r Resource) map[string]string { return fetchCustomHeaders(r, WebhookURLCustomHeaderCanonicalBaseKey) } diff --git a/internal/app/xhttp/pkg/resource/header_test.go b/internal/app/xhttp/pkg/resource/header_test.go index 07c71f0a..5f9856ee 100644 --- a/internal/app/xhttp/pkg/resource/header_test.go +++ b/internal/app/xhttp/pkg/resource/header_test.go @@ -18,17 +18,13 @@ func TestRemoteURLCustomHeaders(t *testing.T) { 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, - }, + r.WithCustomHeader(customHeaderCanonicalKey, customHeaderValue) + r.WithCustomHeader("Bar", "Bar") + expected := map[string]string{ + customHeaderCanonicalRealKey: customHeaderValue, } - notExpected := map[string][]string{ - customHeaderCanonicalKey: []string{ - customHeaderValue, - }, + notExpected := map[string]string{ + customHeaderCanonicalKey: customHeaderValue, } v := RemoteURLCustomHeaders(r) assert.Equal(t, expected, v) @@ -44,17 +40,13 @@ func TestWebhookURLCustomHeaders(t *testing.T) { 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, - }, + r.WithCustomHeader(customHeaderCanonicalKey, customHeaderValue) + r.WithCustomHeader("Bar", "Bar") + expected := map[string]string{ + customHeaderCanonicalRealKey: customHeaderValue, } - notExpected := map[string][]string{ - customHeaderCanonicalKey: []string{ - customHeaderValue, - }, + notExpected := map[string]string{ + customHeaderCanonicalKey: customHeaderValue, } v := WebhookURLCustomHeaders(r) assert.Equal(t, expected, v) diff --git a/internal/app/xhttp/pkg/resource/resource.go b/internal/app/xhttp/pkg/resource/resource.go index 39d1229b..29d1241d 100644 --- a/internal/app/xhttp/pkg/resource/resource.go +++ b/internal/app/xhttp/pkg/resource/resource.go @@ -3,6 +3,7 @@ package resource import ( "fmt" "io" + "net/http" "os" "path/filepath" "strings" @@ -25,7 +26,7 @@ const TemporaryDirectory string = "tmp" type Resource struct { logger xlog.Logger dirPath string - customHeaders map[string][]string + customHeaders map[string]string args map[ArgKey]string files map[string]file } @@ -53,7 +54,7 @@ func New(logger xlog.Logger, directoryName string) (Resource, error) { return Resource{ logger: logger, dirPath: dirPath, - customHeaders: make(map[string][]string), + customHeaders: make(map[string]string), args: make(map[ArgKey]string), files: make(map[string]file), }, nil @@ -76,15 +77,17 @@ func (r Resource) Close() error { // WithCustomHeader add a new custom header to the Resource. // Given key should be in canonical format. -func (r *Resource) WithCustomHeader(key string, value []string) { +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) + // should already be in canonical format. + canonicalKey := http.CanonicalHeaderKey(key) + if strings.Contains(canonicalKey, RemoteURLCustomHeaderCanonicalBaseKey) || + strings.Contains(canonicalKey, WebhookURLCustomHeaderCanonicalBaseKey) { + r.customHeaders[canonicalKey] = value + r.logger.DebugfOp(op, "added '%s' with value '%s' to resource custom headers", canonicalKey, value) return } - r.logger.DebugfOp(op, "skipping '%s' as it is not a custom header...", key) + r.logger.DebugfOp(op, "skipping '%s' as it is not a custom header...", canonicalKey) } // WithArg add a new argument to the Resource. diff --git a/internal/pkg/printer/chrome.go b/internal/pkg/printer/chrome.go index 8f1cc473..dce45a05 100644 --- a/internal/pkg/printer/chrome.go +++ b/internal/pkg/printer/chrome.go @@ -2,6 +2,7 @@ package printer import ( "context" + "encoding/json" "fmt" "io/ioutil" "strings" @@ -42,6 +43,7 @@ type ChromePrinterOptions struct { MarginRight float64 Landscape bool RpccBufferSize int64 + CustomHeaders map[string]string } // DefaultChromePrinterOptions returns the default @@ -61,6 +63,7 @@ func DefaultChromePrinterOptions(config conf.Config) ChromePrinterOptions { MarginRight: 1.0, Landscape: false, RpccBufferSize: config.DefaultGoogleChromeRpccBufferSize(), + CustomHeaders: make(map[string]string), } } @@ -144,6 +147,10 @@ func (p chromePrinter) Print(destination string) error { if err := p.enableEvents(ctx, targetClient); err != nil { return err } + // add custom headers (if any). + if err := p.setCustomHeaders(ctx, targetClient); err != nil { + return err + } // listen for all events. if err := p.listenEvents(ctx, targetClient); err != nil { return err @@ -247,6 +254,32 @@ func (p chromePrinter) enableEvents(ctx context.Context, client *cdp.Client) err return nil } +func (p chromePrinter) setCustomHeaders(ctx context.Context, client *cdp.Client) error { + const op string = "printer.chromePrinter.setCustomHeaders" + resolver := func() error { + if len(p.opts.CustomHeaders) == 0 { + p.logger.DebugOp(op, "skipping custom headers as none have been provided...") + return nil + } + customHeaders := make(map[string]string) + // useless but for the logs. + for key, value := range p.opts.CustomHeaders { + customHeaders[key] = value + p.logger.DebugfOp(op, "set '%s' to custom header '%s'", value, key) + } + b, err := json.Marshal(customHeaders) + if err != nil { + return err + } + // should always be called after client.Network.Enable. + return client.Network.SetExtraHTTPHeaders(ctx, network.NewSetExtraHTTPHeadersArgs(b)) + } + if err := resolver(); err != nil { + return xerror.New(op, err) + } + return nil +} + func (p chromePrinter) listenEvents(ctx context.Context, client *cdp.Client) error { const op string = "printer.chromePrinter.listenEvents" resolver := func() error { diff --git a/test/multipartform.go b/test/multipartform.go index 0bc73508..00e8e552 100644 --- a/test/multipartform.go +++ b/test/multipartform.go @@ -79,7 +79,7 @@ func multipartForm( require.Nil(t, err) } if kind == "url" { - err := writer.WriteField("remoteURL", "http://google.com") + err := writer.WriteField("remoteURL", "https://google.com") require.Nil(t, err) } for k, v := range formValues { From cf15e3a9a1036decb553ce5f988eac0e63dbfb07 Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Fri, 6 Dec 2019 16:18:14 +0100 Subject: [PATCH 4/7] updating documentation (without go & php examples) --- build/docs/content/05-url.md | 32 +++++++++++ build/docs/content/10-webhook.md | 40 +++++++++++-- docs/index.html | 98 ++++++++++++++++++++++++++++---- 3 files changed, 155 insertions(+), 15 deletions(-) diff --git a/build/docs/content/05-url.md b/build/docs/content/05-url.md index 6517843a..7795b833 100644 --- a/build/docs/content/05-url.md +++ b/build/docs/content/05-url.md @@ -54,3 +54,35 @@ $request->setMargins(Request::NO_MARGINS); $dest = "result.pdf"; $client->store($request, $dest); ``` + +## Custom HTTP headers + +You may send your own HTTP headers to the `remoteURL`. + +For instance, by adding the HTTP header `Gotenberg-Remoteurl-Your-Header` to your request, +the API will send a request to the `remoteURL` with the HTTP header `Your-Header`. + +> **Attention:** the API uses a canonical format for the HTTP headers: +> it transforms the first +> letter and any letter following a hyphen to upper case; +> the rest are converted to lowercase. For example, the +> canonical key for `accept-encoding` is `Accept-Encoding`. + +### cURL + +```bash +$ curl --request POST \ + --url http://localhost:3000/convert/url \ + --header 'Content-Type: multipart/form-data' \ + --header 'Gotenberg-Remoteurl-Your-Header: Foo' \ + --form remoteURL=https://google.com \ + -o result.pdf +``` + +### Go + +// TODO + +### PHP + +// TODO \ No newline at end of file diff --git a/build/docs/content/10-webhook.md b/build/docs/content/10-webhook.md index fff2456d..dab6104f 100644 --- a/build/docs/content/10-webhook.md +++ b/build/docs/content/10-webhook.md @@ -58,9 +58,7 @@ It takes a float as value (e.g `2.5` for 2.5 seconds). > You may also define this value globally: see the [environment variables](#environment_variables.default_webhook_url_timeout) section. -### Examples - -#### cURL +### cURL ```bash $ curl --request POST \ @@ -71,7 +69,7 @@ $ curl --request POST \ --form webhookURLTimeout=2.5 ``` -#### Go +### Go ```golang import "github.com/thecodingmachine/gotenberg-go-client/v6" @@ -85,7 +83,7 @@ func main() { } ``` -#### PHP +### PHP ```php use TheCodingMachine\Gotenberg\Client; @@ -99,3 +97,35 @@ $request->setWebhookURL('http://myapp.com/webhook/'); $request->setWebhookURLTimeout(2.5); $resp = $client->post($request); ``` + +## Custom HTTP headers + +You may send your own HTTP headers to the `webhookURL`. + +For instance, by adding the HTTP header `Gotenberg-Webhookurl-Your-Header` to your request, +the API will send a request to the `webhookURL` with the HTTP header `Your-Header`. + +> **Attention:** the API uses a canonical format for the HTTP headers: +> it transforms the first +> letter and any letter following a hyphen to upper case; +> the rest are converted to lowercase. For example, the +> canonical key for `accept-encoding` is `Accept-Encoding`. + +### cURL + +```bash +$ curl --request POST \ + --url http://localhost:3000/convert/html \ + --header 'Content-Type: multipart/form-data' \ + --header 'Gotenberg-Webhookurl-Your-Header: Foo' \ + --form files=@index.html \ + --form webhookURL='http://myapp.com/webhook/' +``` + +### Go + +// TODO + +### PHP + +// TODO \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 85c3b75a..fb2f54d8 100755 --- a/docs/index.html +++ b/docs/index.html @@ -884,6 +884,47 @@ $dest = "result.pdf"; $client->store($request, $dest); +

Custom HTTP headers

+ +

You may send your own HTTP headers to the remoteURL.

+ +

For instance, by adding the HTTP header Gotenberg-Remoteurl-Your-Header to your request, +the API will send a request to the remoteURL with the HTTP header Your-Header.

+ +
+

Attention: the API uses a canonical format for the HTTP headers: +it transforms the first +letter and any letter following a hyphen to upper case; +the rest are converted to lowercase. For example, the +canonical key for accept-encoding is Accept-Encoding.

+
+ +

cURL

+ +
$ curl --request POST \
+    --url http://localhost:3000/convert/url \
+    --header 'Content-Type: multipart/form-data' \
+    --header 'Gotenberg-Remoteurl-Your-Header: Foo' \
+    --form remoteURL=https://google.com \
+    -o result.pdf
+
+ +

Go

+ +

// TODO

+ +

PHP

+ +

// TODO

+
@@ -1294,13 +1335,9 @@ $resp = $client->post($request);

You may also define this value globally: see the environment variables section.

-

Examples

- -

cURL

+cURL
$ curl --request POST \
     --url http://localhost:3000/convert/html \
@@ -1310,9 +1347,9 @@ $resp = $client->post($request);
     --form webhookURLTimeout=2.5
 
-

Go

+Go

import "github.com/thecodingmachine/gotenberg-go-client/v6"
 
@@ -1325,9 +1362,9 @@ $resp = $client->post($request);
 }
 
-

PHP

+PHP

use TheCodingMachine\Gotenberg\Client;
 use TheCodingMachine\Gotenberg\DocumentFactory;
@@ -1341,6 +1378,47 @@ $request->setWebhookURLTimeout(2.5);
 $resp = $client->post($request);
 
+

Custom HTTP headers

+ +

You may send your own HTTP headers to the webhookURL.

+ +

For instance, by adding the HTTP header Gotenberg-Webhookurl-Your-Header to your request, +the API will send a request to the webhookURL with the HTTP header Your-Header.

+ +
+

Attention: the API uses a canonical format for the HTTP headers: +it transforms the first +letter and any letter following a hyphen to upper case; +the rest are converted to lowercase. For example, the +canonical key for accept-encoding is Accept-Encoding.

+
+ +

cURL

+ +
$ curl --request POST \
+    --url http://localhost:3000/convert/html \
+    --header 'Content-Type: multipart/form-data' \
+    --header 'Gotenberg-Webhookurl-Your-Header: Foo' \
+    --form files=@index.html \
+    --form webhookURL='http://myapp.com/webhook/'
+
+ +

Go

+ +

// TODO

+ +

PHP

+ +

// TODO

+
From 126cdd73e42ab0b7d1baab7ce335bb5485ab083b Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Mon, 9 Dec 2019 16:50:08 +0100 Subject: [PATCH 5/7] adding PHP example for custom HTTP headers in documentation + improving logs for custom HTTP headers --- build/docs/content/02-clients.md | 2 +- build/docs/content/04-html.md | 12 ++-- build/docs/content/05-url.md | 13 +++- build/docs/content/06-markdown.md | 2 +- build/docs/content/07-office.md | 4 +- build/docs/content/08-merge.md | 2 +- build/docs/content/09-timeout.md | 2 +- build/docs/content/10-webhook.md | 13 +++- docs/index.html | 48 +++++++++----- internal/app/xhttp/handler.go | 8 +-- internal/app/xhttp/handler_test.go | 2 +- internal/app/xhttp/pkg/context/context.go | 2 +- internal/app/xhttp/pkg/resource/header.go | 22 +++---- .../app/xhttp/pkg/resource/header_test.go | 16 ++--- internal/app/xhttp/pkg/resource/resource.go | 14 ++-- internal/pkg/printer/chrome.go | 66 +++++++++---------- 16 files changed, 133 insertions(+), 95 deletions(-) diff --git a/build/docs/content/02-clients.md b/build/docs/content/02-clients.md index 560601e6..39bcd0cc 100644 --- a/build/docs/content/02-clients.md +++ b/build/docs/content/02-clients.md @@ -18,7 +18,7 @@ Unless your project already has a PSR7 `HttpClient`, install `php-http/guzzle6-a $ composer require php-http/guzzle6-adapter ``` -Then the PHP client: +Then the [PHP client](https://github.com/thecodingmachine/gotenberg-php-client): ```bash $ composer require thecodingmachine/gotenberg-php-client diff --git a/build/docs/content/04-html.md b/build/docs/content/04-html.md index 060da6ef..00a5fb8c 100644 --- a/build/docs/content/04-html.md +++ b/build/docs/content/04-html.md @@ -59,7 +59,7 @@ use TheCodingMachine\Gotenberg\HTMLRequest; $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); $index = DocumentFactory::makeFromPath('index.html', 'index.html'); $request = new HTMLRequest($index); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` @@ -146,7 +146,7 @@ $footer = DocumentFactory::makeFromPath('footer.html', 'footer.html'); $request = new HTMLRequest($index); $request->setHeader($header); $request->setFooter($footer); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` @@ -237,7 +237,7 @@ $assets = [ ]; $request = new HTMLRequest($index); $request->setAssets($assets); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` @@ -296,7 +296,7 @@ $request = new HTMLRequest($index); $request->setPaperSize(Request::A4); $request->setMargins(Request::NO_MARGINS); $request->setLandscape(true); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` @@ -345,7 +345,7 @@ $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client() $index = DocumentFactory::makeFromPath('index.html', 'index.html'); $request = new HTMLRequest($index); $request->setWaitDelay(5.5); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` @@ -397,6 +397,6 @@ $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client() $index = DocumentFactory::makeFromPath('index.html', 'index.html'); $request = new HTMLRequest($index); $request->setGoogleChromeRpccBufferSize(1048576); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` diff --git a/build/docs/content/05-url.md b/build/docs/content/05-url.md index 7795b833..900f91f5 100644 --- a/build/docs/content/05-url.md +++ b/build/docs/content/05-url.md @@ -51,7 +51,7 @@ use TheCodingMachine\Gotenberg\URLRequest; $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); $request = new URLRequest('https://google.com'); $request->setMargins(Request::NO_MARGINS); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` @@ -85,4 +85,13 @@ $ curl --request POST \ ### PHP -// TODO \ No newline at end of file +```php +use TheCodingMachine\Gotenberg\Client; +use TheCodingMachine\Gotenberg\URLRequest; + +$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); +$request = new URLRequest('https://google.com'); +$request->addRemoteURLHTTPHeader('Your-Header', 'Foo') +$dest = 'result.pdf'; +$client->store($request, $dest); +``` diff --git a/build/docs/content/06-markdown.md b/build/docs/content/06-markdown.md index 66c50ee4..65b71702 100644 --- a/build/docs/content/06-markdown.md +++ b/build/docs/content/06-markdown.md @@ -65,6 +65,6 @@ $markdowns = [ DocumentFactory::makeFromPath('file.md', 'file.md'), ]; $request = new MarkdownRequest($index, $markdowns); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` diff --git a/build/docs/content/07-office.md b/build/docs/content/07-office.md index 39e91e37..70a1e2fa 100644 --- a/build/docs/content/07-office.md +++ b/build/docs/content/07-office.md @@ -64,7 +64,7 @@ $files = [ DocumentFactory::makeFromPath('document2.docx', 'document2.docx'), ]; $request = new OfficeRequest($files); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` @@ -112,6 +112,6 @@ $files = [ ]; $request = new OfficeRequest($files); $request->setLandscape(true); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` diff --git a/build/docs/content/08-merge.md b/build/docs/content/08-merge.md index 49ac0b2b..fc0d50ba 100644 --- a/build/docs/content/08-merge.md +++ b/build/docs/content/08-merge.md @@ -50,6 +50,6 @@ $files = [ DocumentFactory::makeFromPath('file2.pdf', 'file2.pdf'), ]; $request = new MergeRequest($files); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` diff --git a/build/docs/content/09-timeout.md b/build/docs/content/09-timeout.md index 9392b84f..7c644cec 100644 --- a/build/docs/content/09-timeout.md +++ b/build/docs/content/09-timeout.md @@ -48,6 +48,6 @@ $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client() $index = DocumentFactory::makeFromPath('index.html', 'index.html'); $request = new HTMLRequest($index); $request->setWaitTimeout(2.5); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); ``` diff --git a/build/docs/content/10-webhook.md b/build/docs/content/10-webhook.md index dab6104f..2824e192 100644 --- a/build/docs/content/10-webhook.md +++ b/build/docs/content/10-webhook.md @@ -128,4 +128,15 @@ $ curl --request POST \ ### PHP -// TODO \ No newline at end of file +```php +use TheCodingMachine\Gotenberg\Client; +use TheCodingMachine\Gotenberg\DocumentFactory; +use TheCodingMachine\Gotenberg\HTMLRequest; + +$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); +$index = DocumentFactory::makeFromPath('index.html', 'index.html'); +$request = new HTMLRequest($index); +$request->setWebhookURL('http://myapp.com/webhook/'); +$request->addWebhookURLHTTPHeader('Your-Header', 'Foo'); +$resp = $client->post($request); +``` \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index a762b4a3..63fe13fe 100755 --- a/docs/index.html +++ b/docs/index.html @@ -233,7 +233,7 @@ Gotenberg API is available at http://localhost:3
$ composer require php-http/guzzle6-adapter
 
-

Then the PHP client:

+

Then the PHP client:

$ composer require thecodingmachine/gotenberg-php-client
 
@@ -474,7 +474,7 @@ use TheCodingMachine\Gotenberg\HTMLRequest; $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); $index = DocumentFactory::makeFromPath('index.html', 'index.html'); $request = new HTMLRequest($index); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -569,7 +569,7 @@ $footer = DocumentFactory::makeFromPath('footer.html', 'footer.html& $request = new HTMLRequest($index); $request->setHeader($header); $request->setFooter($footer); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -665,7 +665,7 @@ $assets = [ ]; $request = new HTMLRequest($index); $request->setAssets($assets); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -731,7 +731,7 @@ $request = new HTMLRequest($index); $request->setPaperSize(Request::A4); $request->setMargins(Request::NO_MARGINS); $request->setLandscape(true); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -787,7 +787,7 @@ $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\ $index = DocumentFactory::makeFromPath('index.html', 'index.html'); $request = new HTMLRequest($index); $request->setWaitDelay(5.5); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -846,7 +846,7 @@ $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\ $index = DocumentFactory::makeFromPath('index.html', 'index.html'); $request = new HTMLRequest($index); $request->setGoogleChromeRpccBufferSize(1048576); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -912,7 +912,7 @@ use TheCodingMachine\Gotenberg\URLRequest; $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); $request = new URLRequest('https://google.com'); $request->setMargins(Request::NO_MARGINS); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -955,7 +955,15 @@ canonical key for accept-encoding is Accept-Encoding.< PHP -

// TODO

+
use TheCodingMachine\Gotenberg\Client;
+use TheCodingMachine\Gotenberg\URLRequest;
+
+$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
+$request = new URLRequest('https://google.com');
+$request->addRemoteURLHTTPHeader('Your-Header', 'Foo')
+$dest = 'result.pdf';
+$client->store($request, $dest);
+
@@ -1030,7 +1038,7 @@ $markdowns = [ DocumentFactory::makeFromPath('file.md', 'file.md'), ]; $request = new MarkdownRequest($index, $markdowns); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -1111,7 +1119,7 @@ $files = [ DocumentFactory::makeFromPath('document2.docx', 'document2.docx'), ]; $request = new OfficeRequest($files); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -1164,7 +1172,7 @@ $files = [ ]; $request = new OfficeRequest($files); $request->setLandscape(true); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -1229,7 +1237,7 @@ $files = [ DocumentFactory::makeFromPath('file2.pdf', 'file2.pdf'), ]; $request = new MergeRequest($files); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -1292,7 +1300,7 @@ $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\ $index = DocumentFactory::makeFromPath('index.html', 'index.html'); $request = new HTMLRequest($index); $request->setWaitTimeout(2.5); -$dest = "result.pdf"; +$dest = 'result.pdf'; $client->store($request, $dest); @@ -1449,7 +1457,17 @@ canonical key for accept-encoding is Accept-Encoding.< PHP -

// TODO

+
use TheCodingMachine\Gotenberg\Client;
+use TheCodingMachine\Gotenberg\DocumentFactory;
+use TheCodingMachine\Gotenberg\HTMLRequest;
+
+$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
+$index = DocumentFactory::makeFromPath('index.html', 'index.html');
+$request = new HTMLRequest($index);
+$request->setWebhookURL('http://myapp.com/webhook/');
+$request->addWebhookURLHTTPHeader('Your-Header', 'Foo');
+$resp = $client->post($request);
+
diff --git a/internal/app/xhttp/handler.go b/internal/app/xhttp/handler.go index 07959870..e15f6443 100644 --- a/internal/app/xhttp/handler.go +++ b/internal/app/xhttp/handler.go @@ -138,7 +138,7 @@ func urlHandler(c echo.Context) error { if err != nil { return err } - opts.CustomHeaders = resource.RemoteURLCustomHeaders(r) + opts.CustomHTTPHeaders = resource.RemoteURLCustomHTTPHeaders(r) if !r.HasArg(resource.RemoteURLArgKey) { return xerror.Invalid( op, @@ -338,14 +338,14 @@ func convertAsync(ctx context.Context, p printer.Printer, filename, fpath string } req.Header.Set(echo.HeaderContentType, "application/pdf") // set custom headers (if any). - customHeaders := resource.WebhookURLCustomHeaders(r) + customHeaders := resource.WebhookURLCustomHTTPHeaders(r) if len(customHeaders) > 0 { for key, value := range customHeaders { req.Header.Set(key, value) - logger.DebugfOp(op, "set '%s' to custom header '%s'", value, key) + logger.DebugfOp(op, "set '%s' to custom HTTP header '%s'", value, key) } } else { - logger.DebugOp(op, "skipping custom headers as none have been provided...") + logger.DebugOp(op, "skipping custom HTTP headers as none have been provided...") } // send the result file. logger.DebugfOp( diff --git a/internal/app/xhttp/handler_test.go b/internal/app/xhttp/handler_test.go index 792569ad..7e141d53 100644 --- a/internal/app/xhttp/handler_test.go +++ b/internal/app/xhttp/handler_test.go @@ -582,7 +582,7 @@ func TestOfficeHandler(t *testing.T) { func TestWebhook(t *testing.T) { customHeaderRealKey := http.CanonicalHeaderKey("MyCustomHeader") - customHeaderKey := fmt.Sprintf("%s%s", resource.WebhookURLCustomHeaderCanonicalBaseKey, customHeaderRealKey) + customHeaderKey := fmt.Sprintf("%s%s", resource.WebhookURLCustomHTTPHeaderCanonicalBaseKey, customHeaderRealKey) customHeaderValue := "foo" status := make(chan error, 2) rcv := echo.New() diff --git a/internal/app/xhttp/pkg/context/context.go b/internal/app/xhttp/pkg/context/context.go index 41f430fb..b8e0b892 100644 --- a/internal/app/xhttp/pkg/context/context.go +++ b/internal/app/xhttp/pkg/context/context.go @@ -80,7 +80,7 @@ func (ctx *Context) WithResource(directoryName string) error { } // retrieve custom headers from request. for key, value := range ctx.Request().Header { - r.WithCustomHeader(key, value[0]) + r.WithCustomHTTPHeader(key, value[0]) } // retrieve form values from request. for _, key := range resource.ArgKeys() { diff --git a/internal/app/xhttp/pkg/resource/header.go b/internal/app/xhttp/pkg/resource/header.go index 9a9745d7..fa4c8814 100644 --- a/internal/app/xhttp/pkg/resource/header.go +++ b/internal/app/xhttp/pkg/resource/header.go @@ -5,15 +5,15 @@ import ( ) const ( - // RemoteURLCustomHeaderCanonicalBaseKey is the base key + // RemoteURLCustomHTTPHeaderCanonicalBaseKey is the base key // of custom headers send to the remote URL. - RemoteURLCustomHeaderCanonicalBaseKey string = "Gotenberg-Remoteurl-" - // WebhookURLCustomHeaderCanonicalBaseKey is the base key + RemoteURLCustomHTTPHeaderCanonicalBaseKey string = "Gotenberg-Remoteurl-" + // WebhookURLCustomHTTPHeaderCanonicalBaseKey is the base key // of custom headers send to the webhook URL. - WebhookURLCustomHeaderCanonicalBaseKey string = "Gotenberg-Webhookurl-" + WebhookURLCustomHTTPHeaderCanonicalBaseKey string = "Gotenberg-Webhookurl-" ) -func fetchCustomHeaders(r Resource, baseKey string) map[string]string { +func fetchCustomHTTPHeaders(r Resource, baseKey string) map[string]string { customHeaders := make(map[string]string) for key, value := range r.customHeaders { if strings.Contains(key, baseKey) { @@ -24,14 +24,14 @@ func fetchCustomHeaders(r Resource, baseKey string) map[string]string { return customHeaders } -// RemoteURLCustomHeaders is a helper for retrieving +// RemoteURLCustomHTTPHeaders is a helper for retrieving // the custom headers for the URL conversion. -func RemoteURLCustomHeaders(r Resource) map[string]string { - return fetchCustomHeaders(r, RemoteURLCustomHeaderCanonicalBaseKey) +func RemoteURLCustomHTTPHeaders(r Resource) map[string]string { + return fetchCustomHTTPHeaders(r, RemoteURLCustomHTTPHeaderCanonicalBaseKey) } -// WebhookURLCustomHeaders is a helper for retrieving +// WebhookURLCustomHTTPHeaders is a helper for retrieving // the custom headers for the webhook URL. -func WebhookURLCustomHeaders(r Resource) map[string]string { - return fetchCustomHeaders(r, WebhookURLCustomHeaderCanonicalBaseKey) +func WebhookURLCustomHTTPHeaders(r Resource) map[string]string { + return fetchCustomHTTPHeaders(r, WebhookURLCustomHTTPHeaderCanonicalBaseKey) } diff --git a/internal/app/xhttp/pkg/resource/header_test.go b/internal/app/xhttp/pkg/resource/header_test.go index 5f9856ee..35a944f8 100644 --- a/internal/app/xhttp/pkg/resource/header_test.go +++ b/internal/app/xhttp/pkg/resource/header_test.go @@ -17,16 +17,16 @@ func TestRemoteURLCustomHeaders(t *testing.T) { // should find the custom header. customHeaderValue := "bar" customHeaderCanonicalRealKey := "Foo" - customHeaderCanonicalKey := http.CanonicalHeaderKey(fmt.Sprintf("%s%s", RemoteURLCustomHeaderCanonicalBaseKey, customHeaderCanonicalRealKey)) - r.WithCustomHeader(customHeaderCanonicalKey, customHeaderValue) - r.WithCustomHeader("Bar", "Bar") + customHeaderCanonicalKey := http.CanonicalHeaderKey(fmt.Sprintf("%s%s", RemoteURLCustomHTTPHeaderCanonicalBaseKey, customHeaderCanonicalRealKey)) + r.WithCustomHTTPHeader(customHeaderCanonicalKey, customHeaderValue) + r.WithCustomHTTPHeader("Bar", "Bar") expected := map[string]string{ customHeaderCanonicalRealKey: customHeaderValue, } notExpected := map[string]string{ customHeaderCanonicalKey: customHeaderValue, } - v := RemoteURLCustomHeaders(r) + v := RemoteURLCustomHTTPHeaders(r) assert.Equal(t, expected, v) assert.NotEqual(t, notExpected, v) } @@ -39,16 +39,16 @@ func TestWebhookURLCustomHeaders(t *testing.T) { // should find the custom header. customHeaderValue := "bar" customHeaderCanonicalRealKey := "Foo" - customHeaderCanonicalKey := http.CanonicalHeaderKey(fmt.Sprintf("%s%s", WebhookURLCustomHeaderCanonicalBaseKey, customHeaderCanonicalRealKey)) - r.WithCustomHeader(customHeaderCanonicalKey, customHeaderValue) - r.WithCustomHeader("Bar", "Bar") + customHeaderCanonicalKey := http.CanonicalHeaderKey(fmt.Sprintf("%s%s", WebhookURLCustomHTTPHeaderCanonicalBaseKey, customHeaderCanonicalRealKey)) + r.WithCustomHTTPHeader(customHeaderCanonicalKey, customHeaderValue) + r.WithCustomHTTPHeader("Bar", "Bar") expected := map[string]string{ customHeaderCanonicalRealKey: customHeaderValue, } notExpected := map[string]string{ customHeaderCanonicalKey: customHeaderValue, } - v := WebhookURLCustomHeaders(r) + v := WebhookURLCustomHTTPHeaders(r) assert.Equal(t, expected, v) assert.NotEqual(t, notExpected, v) } diff --git a/internal/app/xhttp/pkg/resource/resource.go b/internal/app/xhttp/pkg/resource/resource.go index 29d1241d..3a983f18 100644 --- a/internal/app/xhttp/pkg/resource/resource.go +++ b/internal/app/xhttp/pkg/resource/resource.go @@ -75,19 +75,19 @@ func (r Resource) Close() error { return nil } -// WithCustomHeader add a new custom header to the Resource. +// WithCustomHTTPHeader 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" +func (r *Resource) WithCustomHTTPHeader(key string, value string) { + const op string = "resource.Resource.WithCustomHTTPHeader" // should already be in canonical format. canonicalKey := http.CanonicalHeaderKey(key) - if strings.Contains(canonicalKey, RemoteURLCustomHeaderCanonicalBaseKey) || - strings.Contains(canonicalKey, WebhookURLCustomHeaderCanonicalBaseKey) { + if strings.Contains(canonicalKey, RemoteURLCustomHTTPHeaderCanonicalBaseKey) || + strings.Contains(canonicalKey, WebhookURLCustomHTTPHeaderCanonicalBaseKey) { r.customHeaders[canonicalKey] = value - r.logger.DebugfOp(op, "added '%s' with value '%s' to resource custom headers", canonicalKey, value) + r.logger.DebugfOp(op, "added '%s' with value '%s' to resource custom HTTP headers", canonicalKey, value) return } - r.logger.DebugfOp(op, "skipping '%s' as it is not a custom header...", canonicalKey) + r.logger.DebugfOp(op, "skipping '%s' as it is not a custom HTTP header...", canonicalKey) } // WithArg add a new argument to the Resource. diff --git a/internal/pkg/printer/chrome.go b/internal/pkg/printer/chrome.go index dce45a05..71e16b32 100644 --- a/internal/pkg/printer/chrome.go +++ b/internal/pkg/printer/chrome.go @@ -31,19 +31,19 @@ type chromePrinter struct { // ChromePrinterOptions helps customizing the // Google Chrome Printer behaviour. type ChromePrinterOptions struct { - WaitTimeout float64 - WaitDelay float64 - HeaderHTML string - FooterHTML string - PaperWidth float64 - PaperHeight float64 - MarginTop float64 - MarginBottom float64 - MarginLeft float64 - MarginRight float64 - Landscape bool - RpccBufferSize int64 - CustomHeaders map[string]string + WaitTimeout float64 + WaitDelay float64 + HeaderHTML string + FooterHTML string + PaperWidth float64 + PaperHeight float64 + MarginTop float64 + MarginBottom float64 + MarginLeft float64 + MarginRight float64 + Landscape bool + RpccBufferSize int64 + CustomHTTPHeaders map[string]string } // DefaultChromePrinterOptions returns the default @@ -51,19 +51,19 @@ type ChromePrinterOptions struct { func DefaultChromePrinterOptions(config conf.Config) ChromePrinterOptions { const defaultHeaderFooterHTML string = "" return ChromePrinterOptions{ - WaitTimeout: config.DefaultWaitTimeout(), - WaitDelay: 0.0, - HeaderHTML: defaultHeaderFooterHTML, - FooterHTML: defaultHeaderFooterHTML, - PaperWidth: 8.27, - PaperHeight: 11.7, - MarginTop: 1.0, - MarginBottom: 1.0, - MarginLeft: 1.0, - MarginRight: 1.0, - Landscape: false, - RpccBufferSize: config.DefaultGoogleChromeRpccBufferSize(), - CustomHeaders: make(map[string]string), + WaitTimeout: config.DefaultWaitTimeout(), + WaitDelay: 0.0, + HeaderHTML: defaultHeaderFooterHTML, + FooterHTML: defaultHeaderFooterHTML, + PaperWidth: 8.27, + PaperHeight: 11.7, + MarginTop: 1.0, + MarginBottom: 1.0, + MarginLeft: 1.0, + MarginRight: 1.0, + Landscape: false, + RpccBufferSize: config.DefaultGoogleChromeRpccBufferSize(), + CustomHTTPHeaders: make(map[string]string), } } @@ -148,7 +148,7 @@ func (p chromePrinter) Print(destination string) error { return err } // add custom headers (if any). - if err := p.setCustomHeaders(ctx, targetClient); err != nil { + if err := p.setCustomHTTPHeaders(ctx, targetClient); err != nil { return err } // listen for all events. @@ -254,18 +254,18 @@ func (p chromePrinter) enableEvents(ctx context.Context, client *cdp.Client) err return nil } -func (p chromePrinter) setCustomHeaders(ctx context.Context, client *cdp.Client) error { - const op string = "printer.chromePrinter.setCustomHeaders" +func (p chromePrinter) setCustomHTTPHeaders(ctx context.Context, client *cdp.Client) error { + const op string = "printer.chromePrinter.setCustomHTTPHeaders" resolver := func() error { - if len(p.opts.CustomHeaders) == 0 { - p.logger.DebugOp(op, "skipping custom headers as none have been provided...") + if len(p.opts.CustomHTTPHeaders) == 0 { + p.logger.DebugOp(op, "skipping custom HTTP headers as none have been provided...") return nil } customHeaders := make(map[string]string) // useless but for the logs. - for key, value := range p.opts.CustomHeaders { + for key, value := range p.opts.CustomHTTPHeaders { customHeaders[key] = value - p.logger.DebugfOp(op, "set '%s' to custom header '%s'", value, key) + p.logger.DebugfOp(op, "set '%s' to custom HTTP header '%s'", value, key) } b, err := json.Marshal(customHeaders) if err != nil { From 1eeb5fc2b0c0aec6281d4d4bf6a21f2a5e649cc5 Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Mon, 9 Dec 2019 17:20:56 +0100 Subject: [PATCH 6/7] typo in variable --- internal/app/xhttp/handler.go | 6 +++--- internal/pkg/printer/chrome.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/app/xhttp/handler.go b/internal/app/xhttp/handler.go index e15f6443..92cf22ea 100644 --- a/internal/app/xhttp/handler.go +++ b/internal/app/xhttp/handler.go @@ -338,9 +338,9 @@ func convertAsync(ctx context.Context, p printer.Printer, filename, fpath string } req.Header.Set(echo.HeaderContentType, "application/pdf") // set custom headers (if any). - customHeaders := resource.WebhookURLCustomHTTPHeaders(r) - if len(customHeaders) > 0 { - for key, value := range customHeaders { + customHTTPHeaders := resource.WebhookURLCustomHTTPHeaders(r) + if len(customHTTPHeaders) > 0 { + for key, value := range customHTTPHeaders { req.Header.Set(key, value) logger.DebugfOp(op, "set '%s' to custom HTTP header '%s'", value, key) } diff --git a/internal/pkg/printer/chrome.go b/internal/pkg/printer/chrome.go index 71e16b32..10337fab 100644 --- a/internal/pkg/printer/chrome.go +++ b/internal/pkg/printer/chrome.go @@ -261,13 +261,13 @@ func (p chromePrinter) setCustomHTTPHeaders(ctx context.Context, client *cdp.Cli p.logger.DebugOp(op, "skipping custom HTTP headers as none have been provided...") return nil } - customHeaders := make(map[string]string) + customHTTPHeaders := make(map[string]string) // useless but for the logs. for key, value := range p.opts.CustomHTTPHeaders { - customHeaders[key] = value + customHTTPHeaders[key] = value p.logger.DebugfOp(op, "set '%s' to custom HTTP header '%s'", value, key) } - b, err := json.Marshal(customHeaders) + b, err := json.Marshal(customHTTPHeaders) if err != nil { return err } From 81e7bab22a0602c04915c72059f613eecd958688 Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Mon, 9 Dec 2019 17:26:00 +0100 Subject: [PATCH 7/7] updating documentation with Golang examples for custom HTTP headers --- build/docs/content/05-url.md | 12 +++++++++++- build/docs/content/10-webhook.md | 12 +++++++++++- docs/index.html | 22 ++++++++++++++++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/build/docs/content/05-url.md b/build/docs/content/05-url.md index 900f91f5..16c42b83 100644 --- a/build/docs/content/05-url.md +++ b/build/docs/content/05-url.md @@ -81,7 +81,17 @@ $ curl --request POST \ ### Go -// TODO +```golang +import "github.com/thecodingmachine/gotenberg-go-client/v6" + +func main() { + c := &gotenberg.Client{Hostname: "http://localhost:3000"} + req := gotenberg.NewURLRequest("https://google.com") + req.AddRemoteURLHTTPHeader("Your-Header", "Foo") + dest := "result.pdf" + c.Store(req, dest) +} +``` ### PHP diff --git a/build/docs/content/10-webhook.md b/build/docs/content/10-webhook.md index 2824e192..06c97f24 100644 --- a/build/docs/content/10-webhook.md +++ b/build/docs/content/10-webhook.md @@ -124,7 +124,17 @@ $ curl --request POST \ ### Go -// TODO +```golang +import "github.com/thecodingmachine/gotenberg-go-client/v6" + +func main() { + c := &gotenberg.Client{Hostname: "http://localhost:3000"} + req, _ := gotenberg.NewHTMLRequest("index.html") + req.WebhookURL("http://myapp.com/webhook/") + req.AddWebhookURLHTTPHeader("Your-Header", "Foo") + resp, _ := c.Post(req) +} +``` ### PHP diff --git a/docs/index.html b/docs/index.html index 63fe13fe..95af3d47 100755 --- a/docs/index.html +++ b/docs/index.html @@ -949,7 +949,16 @@ canonical key for accept-encoding is Accept-Encoding.< Go -

// TODO

+
import "github.com/thecodingmachine/gotenberg-go-client/v6"
+
+func main() {
+    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
+    req := gotenberg.NewURLRequest("https://google.com")
+    req.AddRemoteURLHTTPHeader("Your-Header", "Foo")
+    dest := "result.pdf"
+    c.Store(req, dest)
+}
+

Go

-

// TODO

+
import "github.com/thecodingmachine/gotenberg-go-client/v6"
+
+func main() {
+    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
+    req, _ := gotenberg.NewHTMLRequest("index.html")
+    req.WebhookURL("http://myapp.com/webhook/")
+    req.AddWebhookURLHTTPHeader("Your-Header", "Foo")
+    resp, _ := c.Post(req)
+}
+