mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-07-02 00:17:40 +08: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)
|
||||
}
|
||||
|
||||
var (
|
||||
connectionRefused error
|
||||
connectionRefusedMu sync.RWMutex
|
||||
)
|
||||
|
||||
// See https://github.com/gotenberg/gotenberg/issues/913.
|
||||
listenForEventLoadingFailedOnConnectionRefused(taskCtx, logger, &connectionRefused, &connectionRefusedMu)
|
||||
|
||||
err = chromedp.Run(taskCtx, tasks...)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
@@ -249,6 +249,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
|
||||
browser browser
|
||||
fs *gotenberg.FileSystem
|
||||
options PdfOptions
|
||||
url string
|
||||
noDeadline bool
|
||||
start bool
|
||||
expectError bool
|
||||
@@ -478,6 +479,32 @@ func TestChromiumBrowser_pdf(t *testing.T) {
|
||||
expectError: true,
|
||||
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",
|
||||
browser: newChromiumBrowser(
|
||||
@@ -1243,10 +1270,15 @@ func TestChromiumBrowser_pdf(t *testing.T) {
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("file://%s/index.html", tc.fs.WorkingDirPath())
|
||||
if tc.url != "" {
|
||||
url = tc.url
|
||||
}
|
||||
|
||||
err := tc.browser.pdf(
|
||||
ctx,
|
||||
logger,
|
||||
fmt.Sprintf("file://%s/index.html", tc.fs.WorkingDirPath()),
|
||||
url,
|
||||
fmt.Sprintf("%s/%s.pdf", tc.fs.WorkingDirPath(), uuid.NewString()),
|
||||
tc.options,
|
||||
)
|
||||
@@ -1286,6 +1318,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
|
||||
browser browser
|
||||
fs *gotenberg.FileSystem
|
||||
options ScreenshotOptions
|
||||
url string
|
||||
noDeadline bool
|
||||
start bool
|
||||
expectError bool
|
||||
@@ -1519,6 +1552,33 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
|
||||
expectError: true,
|
||||
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",
|
||||
browser: newChromiumBrowser(
|
||||
@@ -2169,10 +2229,15 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("file://%s/index.html", tc.fs.WorkingDirPath())
|
||||
if tc.url != "" {
|
||||
url = tc.url
|
||||
}
|
||||
|
||||
err := tc.browser.screenshot(
|
||||
ctx,
|
||||
logger,
|
||||
fmt.Sprintf("file://%s/index.html", tc.fs.WorkingDirPath()),
|
||||
url,
|
||||
fmt.Sprintf("%s/%s.pdf", tc.fs.WorkingDirPath(), uuid.NewString()),
|
||||
tc.options,
|
||||
)
|
||||
|
||||
@@ -42,6 +42,9 @@ var (
|
||||
// is set to true.
|
||||
ErrConsoleExceptions = errors.New("console exceptions")
|
||||
|
||||
// ErrConnectionRefused happens when a URL cannot be reached.
|
||||
ErrConnectionRefused = errors.New("connection refused")
|
||||
|
||||
// PDF specific.
|
||||
|
||||
// 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
|
||||
// returned by the main page.
|
||||
// listenForEventResponseReceived listens for an invalid HTTP status code that
|
||||
// is returned by the main page.
|
||||
// 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) {
|
||||
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
|
||||
// appends those exceptions to the given error pointer.
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -1435,6 +1435,18 @@ func TestConvertUrl(t *testing.T) {
|
||||
expectHttpStatus: http.StatusConflict,
|
||||
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",
|
||||
ctx: &api.ContextMock{Context: new(api.Context)},
|
||||
@@ -1633,6 +1645,18 @@ func TestScreenshotUrl(t *testing.T) {
|
||||
expectHttpStatus: http.StatusConflict,
|
||||
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",
|
||||
ctx: &api.ContextMock{Context: new(api.Context)},
|
||||
|
||||
Reference in New Issue
Block a user