Summary
AuthRoundCube::sendRequest() only configures CURLOPT_TIMEOUT for POST requests which contain request data.
GET requests have no explicit total timeout, and no request method has an explicit connection timeout.
Opening mail_roundcube performs server-side HTTP requests to Roundcube, so an unreachable or partially responsive Roundcube/reverse-proxy endpoint can hold a Nextcloud web request for an unnecessarily long time.
Steps to reproduce
- Configure a valid Roundcube
externalLocation.
- Make that endpoint accept a connection but stop responding before the HTTP request completes.
- Open the Roundcube app in Nextcloud.
- Observe the server-side Roundcube request.
This can also be verified directly by inspecting AuthRoundCube::sendRequest().
Expected behavior
Every HTTP request to the external Roundcube instance should have:
- a finite connection timeout; and
- a finite overall request timeout.
If Roundcube is unavailable, mail_roundcube should fail within a bounded amount of time and return its normal error state.
Actual behavior
The common cURL options have no timeout:
$curlOpts = [
CURLOPT_URL => $rcQuery,
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FRESH_CONNECT => true
];
CURLOPT_TIMEOUT is added only inside the POST-with-data branch:
if ($method === 'POST') {
$curlOpts[CURLOPT_POST] = true;
if ($data) {
...
$curlOpts[CURLOPT_TIMEOUT] = 60;
}
} else {
$curlOpts[CURLOPT_HTTPGET] = true;
}
Therefore GET requests have no application-defined timeout.
Cause
Timeout configuration is nested inside the POST-with-data path rather than being part of the common cURL configuration.
Several operations used during normal login/session handling are GET requests.
Proposed fix
Apply finite timeout values to all requests, for example:
$curlOpts = [
CURLOPT_URL => $rcQuery,
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FRESH_CONNECT => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 60,
];
The exact timeout values are a policy choice; the important point is that both GET and POST requests have bounded connection and total durations.
Environment
- Nextcloud: 33.0.6
- PHP: 8.3.32
- mail_roundcube: 1.3.0 / current NC33 source
- Roundcube: 1.7.3
Summary
AuthRoundCube::sendRequest()only configuresCURLOPT_TIMEOUTfor POST requests which contain request data.GET requests have no explicit total timeout, and no request method has an explicit connection timeout.
Opening mail_roundcube performs server-side HTTP requests to Roundcube, so an unreachable or partially responsive Roundcube/reverse-proxy endpoint can hold a Nextcloud web request for an unnecessarily long time.
Steps to reproduce
externalLocation.This can also be verified directly by inspecting
AuthRoundCube::sendRequest().Expected behavior
Every HTTP request to the external Roundcube instance should have:
If Roundcube is unavailable, mail_roundcube should fail within a bounded amount of time and return its normal error state.
Actual behavior
The common cURL options have no timeout:
CURLOPT_TIMEOUTis added only inside the POST-with-data branch:Therefore GET requests have no application-defined timeout.
Cause
Timeout configuration is nested inside the POST-with-data path rather than being part of the common cURL configuration.
Several operations used during normal login/session handling are GET requests.
Proposed fix
Apply finite timeout values to all requests, for example:
The exact timeout values are a policy choice; the important point is that both GET and POST requests have bounded connection and total durations.
Environment