mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-15 11:52:14 +01:00
feat(chromium): HTTP Bad Request on net::ERR_CONNECTION_REFUSED
This commit is contained in:
@@ -314,6 +314,14 @@ func (b *chromiumBrowser) do(ctx context.Context, logger *zap.Logger, url string
|
|||||||
listenForEventExceptionThrown(taskCtx, logger, &consoleExceptions, &consoleExceptionsMu)
|
listenForEventExceptionThrown(taskCtx, logger, &consoleExceptions, &consoleExceptionsMu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
connectionRefused error
|
||||||
|
connectionRefusedMu sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// See https://github.com/gotenberg/gotenberg/issues/913.
|
||||||
|
listenForEventLoadingFailedOnConnectionRefused(taskCtx, logger, &connectionRefused, &connectionRefusedMu)
|
||||||
|
|
||||||
err = chromedp.Run(taskCtx, tasks...)
|
err = chromedp.Run(taskCtx, tasks...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errMessage := err.Error()
|
errMessage := err.Error()
|
||||||
@@ -349,6 +357,14 @@ func (b *chromiumBrowser) do(ctx context.Context, logger *zap.Logger, url string
|
|||||||
return fmt.Errorf("%v: %w", consoleExceptions, ErrConsoleExceptions)
|
return fmt.Errorf("%v: %w", consoleExceptions, ErrConsoleExceptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See https://github.com/gotenberg/gotenberg/issues/913.
|
||||||
|
connectionRefusedMu.RLock()
|
||||||
|
defer connectionRefusedMu.RUnlock()
|
||||||
|
|
||||||
|
if connectionRefused != nil {
|
||||||
|
return fmt.Errorf("%v: %w", connectionRefused, ErrConnectionRefused)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
|
|||||||
browser browser
|
browser browser
|
||||||
fs *gotenberg.FileSystem
|
fs *gotenberg.FileSystem
|
||||||
options PdfOptions
|
options PdfOptions
|
||||||
|
url string
|
||||||
noDeadline bool
|
noDeadline bool
|
||||||
start bool
|
start bool
|
||||||
expectError bool
|
expectError bool
|
||||||
@@ -478,6 +479,32 @@ func TestChromiumBrowser_pdf(t *testing.T) {
|
|||||||
expectError: true,
|
expectError: true,
|
||||||
expectedError: ErrConsoleExceptions,
|
expectedError: ErrConsoleExceptions,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: "ErrConnectionRefused",
|
||||||
|
browser: newChromiumBrowser(
|
||||||
|
browserArguments{
|
||||||
|
binPath: os.Getenv("CHROMIUM_BIN_PATH"),
|
||||||
|
wsUrlReadTimeout: 5 * time.Second,
|
||||||
|
allowList: regexp2.MustCompile("", 0),
|
||||||
|
denyList: regexp2.MustCompile("", 0),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
fs: func() *gotenberg.FileSystem {
|
||||||
|
fs := gotenberg.NewFileSystem()
|
||||||
|
|
||||||
|
err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs
|
||||||
|
}(),
|
||||||
|
url: "http://localhost:100",
|
||||||
|
noDeadline: false,
|
||||||
|
start: true,
|
||||||
|
expectError: true,
|
||||||
|
expectedError: ErrConnectionRefused,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
scenario: "clear cache",
|
scenario: "clear cache",
|
||||||
browser: newChromiumBrowser(
|
browser: newChromiumBrowser(
|
||||||
@@ -1243,10 +1270,15 @@ func TestChromiumBrowser_pdf(t *testing.T) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("file://%s/index.html", tc.fs.WorkingDirPath())
|
||||||
|
if tc.url != "" {
|
||||||
|
url = tc.url
|
||||||
|
}
|
||||||
|
|
||||||
err := tc.browser.pdf(
|
err := tc.browser.pdf(
|
||||||
ctx,
|
ctx,
|
||||||
logger,
|
logger,
|
||||||
fmt.Sprintf("file://%s/index.html", tc.fs.WorkingDirPath()),
|
url,
|
||||||
fmt.Sprintf("%s/%s.pdf", tc.fs.WorkingDirPath(), uuid.NewString()),
|
fmt.Sprintf("%s/%s.pdf", tc.fs.WorkingDirPath(), uuid.NewString()),
|
||||||
tc.options,
|
tc.options,
|
||||||
)
|
)
|
||||||
@@ -1286,6 +1318,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
|
|||||||
browser browser
|
browser browser
|
||||||
fs *gotenberg.FileSystem
|
fs *gotenberg.FileSystem
|
||||||
options ScreenshotOptions
|
options ScreenshotOptions
|
||||||
|
url string
|
||||||
noDeadline bool
|
noDeadline bool
|
||||||
start bool
|
start bool
|
||||||
expectError bool
|
expectError bool
|
||||||
@@ -1519,6 +1552,33 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
|
|||||||
expectError: true,
|
expectError: true,
|
||||||
expectedError: ErrConsoleExceptions,
|
expectedError: ErrConsoleExceptions,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: "ErrConnectionRefused",
|
||||||
|
browser: newChromiumBrowser(
|
||||||
|
browserArguments{
|
||||||
|
binPath: os.Getenv("CHROMIUM_BIN_PATH"),
|
||||||
|
wsUrlReadTimeout: 5 * time.Second,
|
||||||
|
allowList: regexp2.MustCompile("", 0),
|
||||||
|
denyList: regexp2.MustCompile("", 0),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
fs: func() *gotenberg.FileSystem {
|
||||||
|
fs := gotenberg.NewFileSystem()
|
||||||
|
|
||||||
|
err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs
|
||||||
|
}(),
|
||||||
|
url: "http://localhost:100",
|
||||||
|
noDeadline: false,
|
||||||
|
start: true,
|
||||||
|
expectError: true,
|
||||||
|
expectedError: ErrConnectionRefused,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
scenario: "clear cache",
|
scenario: "clear cache",
|
||||||
browser: newChromiumBrowser(
|
browser: newChromiumBrowser(
|
||||||
@@ -2169,10 +2229,15 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("file://%s/index.html", tc.fs.WorkingDirPath())
|
||||||
|
if tc.url != "" {
|
||||||
|
url = tc.url
|
||||||
|
}
|
||||||
|
|
||||||
err := tc.browser.screenshot(
|
err := tc.browser.screenshot(
|
||||||
ctx,
|
ctx,
|
||||||
logger,
|
logger,
|
||||||
fmt.Sprintf("file://%s/index.html", tc.fs.WorkingDirPath()),
|
url,
|
||||||
fmt.Sprintf("%s/%s.pdf", tc.fs.WorkingDirPath(), uuid.NewString()),
|
fmt.Sprintf("%s/%s.pdf", tc.fs.WorkingDirPath(), uuid.NewString()),
|
||||||
tc.options,
|
tc.options,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ var (
|
|||||||
// is set to true.
|
// is set to true.
|
||||||
ErrConsoleExceptions = errors.New("console exceptions")
|
ErrConsoleExceptions = errors.New("console exceptions")
|
||||||
|
|
||||||
|
// ErrConnectionRefused happens when a URL cannot be reached.
|
||||||
|
ErrConnectionRefused = errors.New("connection refused")
|
||||||
|
|
||||||
// PDF specific.
|
// PDF specific.
|
||||||
|
|
||||||
// ErrOmitBackgroundWithoutPrintBackground happens if
|
// ErrOmitBackgroundWithoutPrintBackground happens if
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ func listenForEventRequestPaused(ctx context.Context, logger *zap.Logger, allowL
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// listenForEventResponseReceived listens for an invalid HTTP status code is
|
// listenForEventResponseReceived listens for an invalid HTTP status code that
|
||||||
// returned by the main page.
|
// is returned by the main page.
|
||||||
// See https://github.com/gotenberg/gotenberg/issues/613.
|
// See https://github.com/gotenberg/gotenberg/issues/613.
|
||||||
func listenForEventResponseReceived(ctx context.Context, logger *zap.Logger, url string, failOnHttpStatusCodes []int64, invalidHttpStatusCode *error, invalidHttpStatusCodeMu *sync.RWMutex) {
|
func listenForEventResponseReceived(ctx context.Context, logger *zap.Logger, url string, failOnHttpStatusCodes []int64, invalidHttpStatusCode *error, invalidHttpStatusCodeMu *sync.RWMutex) {
|
||||||
for _, code := range []int64{199, 299, 399, 499, 599} {
|
for _, code := range []int64{199, 299, 399, 499, 599} {
|
||||||
@@ -95,6 +95,28 @@ func listenForEventResponseReceived(ctx context.Context, logger *zap.Logger, url
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listenForEventLoadingFailedOnConnectionRefused listens for an event
|
||||||
|
// indicating that the main page failed to load.
|
||||||
|
// See https://github.com/gotenberg/gotenberg/issues/913.
|
||||||
|
func listenForEventLoadingFailedOnConnectionRefused(ctx context.Context, logger *zap.Logger, connectionRefused *error, connectionRefusedMu *sync.RWMutex) {
|
||||||
|
chromedp.ListenTarget(ctx, func(ev interface{}) {
|
||||||
|
switch ev := ev.(type) {
|
||||||
|
case *network.EventLoadingFailed:
|
||||||
|
logger.Debug(fmt.Sprintf("event EventLoadingFailed fired: %+v", ev.ErrorText))
|
||||||
|
|
||||||
|
if ev.ErrorText != "net::ERR_CONNECTION_REFUSED" || ev.Type != network.ResourceTypeDocument {
|
||||||
|
logger.Debug("skip EventLoadingFailed: is not net::ERR_CONNECTION_REFUSED and/or resource type Document")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionRefusedMu.Lock()
|
||||||
|
defer connectionRefusedMu.Unlock()
|
||||||
|
|
||||||
|
*connectionRefused = fmt.Errorf("%s", ev.ErrorText)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// listenForEventExceptionThrown listens for exceptions in the console and
|
// listenForEventExceptionThrown listens for exceptions in the console and
|
||||||
// appends those exceptions to the given error pointer.
|
// appends those exceptions to the given error pointer.
|
||||||
// See https://github.com/gotenberg/gotenberg/issues/262.
|
// See https://github.com/gotenberg/gotenberg/issues/262.
|
||||||
|
|||||||
@@ -688,5 +688,15 @@ func handleChromiumError(err error, options Options) error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errors.Is(err, ErrConnectionRefused) {
|
||||||
|
return api.WrapError(
|
||||||
|
err,
|
||||||
|
api.NewSentinelHttpError(
|
||||||
|
http.StatusBadRequest,
|
||||||
|
"Chromium returned net::ERR_CONNECTION_REFUSED",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1435,6 +1435,18 @@ func TestConvertUrl(t *testing.T) {
|
|||||||
expectHttpStatus: http.StatusConflict,
|
expectHttpStatus: http.StatusConflict,
|
||||||
expectOutputPathsCount: 0,
|
expectOutputPathsCount: 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: "ErrConnectionRefused",
|
||||||
|
ctx: &api.ContextMock{Context: new(api.Context)},
|
||||||
|
api: &ApiMock{PdfMock: func(ctx context.Context, logger *zap.Logger, url, outputPath string, options PdfOptions) error {
|
||||||
|
return ErrConnectionRefused
|
||||||
|
}},
|
||||||
|
options: DefaultPdfOptions(),
|
||||||
|
expectError: true,
|
||||||
|
expectHttpError: true,
|
||||||
|
expectHttpStatus: http.StatusBadRequest,
|
||||||
|
expectOutputPathsCount: 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
scenario: "error from Chromium",
|
scenario: "error from Chromium",
|
||||||
ctx: &api.ContextMock{Context: new(api.Context)},
|
ctx: &api.ContextMock{Context: new(api.Context)},
|
||||||
@@ -1633,6 +1645,18 @@ func TestScreenshotUrl(t *testing.T) {
|
|||||||
expectHttpStatus: http.StatusConflict,
|
expectHttpStatus: http.StatusConflict,
|
||||||
expectOutputPathsCount: 0,
|
expectOutputPathsCount: 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: "ErrConnectionRefused",
|
||||||
|
ctx: &api.ContextMock{Context: new(api.Context)},
|
||||||
|
api: &ApiMock{ScreenshotMock: func(ctx context.Context, logger *zap.Logger, url, outputPath string, options ScreenshotOptions) error {
|
||||||
|
return ErrConnectionRefused
|
||||||
|
}},
|
||||||
|
options: DefaultScreenshotOptions(),
|
||||||
|
expectError: true,
|
||||||
|
expectHttpError: true,
|
||||||
|
expectHttpStatus: http.StatusBadRequest,
|
||||||
|
expectOutputPathsCount: 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
scenario: "error from Chromium",
|
scenario: "error from Chromium",
|
||||||
ctx: &api.ContextMock{Context: new(api.Context)},
|
ctx: &api.ContextMock{Context: new(api.Context)},
|
||||||
|
|||||||
Reference in New Issue
Block a user