Skip to content

Conversation

@DeepDiver1975
Copy link
Member

@DeepDiver1975 DeepDiver1975 commented Mar 11, 2024

Description

Not responding remote servers can cause bad user experience.

How Has This Been Tested?

  • call both endpoints with not existing remote servers

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Database schema changes (next release will require increase of minor version instead of patch)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Technical debt
  • Tests only (no source changes)

Checklist:

  • Code changes
  • Unit tests added
  • Acceptance tests added
  • Documentation ticket raised:
  • Changelog item, see TEMPLATE

@DeepDiver1975 DeepDiver1975 force-pushed the fix/sharing-external-unreachable-hosts branch 2 times, most recently from bb7b5b8 to 7d32e4c Compare March 11, 2024 14:54
@DeepDiver1975 DeepDiver1975 force-pushed the fix/sharing-external-unreachable-hosts branch from 7d32e4c to 6a853e0 Compare March 11, 2024 21:17
Copy link
Contributor

@phil-davis phil-davis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and code-style needs to be fixed

@DeepDiver1975 DeepDiver1975 force-pushed the fix/sharing-external-unreachable-hosts branch from 9f7fae9 to ac07d8c Compare March 12, 2024 07:34
@owncloud owncloud deleted a comment from update-docs bot Mar 12, 2024
@DeepDiver1975 DeepDiver1975 force-pushed the fix/sharing-external-unreachable-hosts branch 2 times, most recently from 952c78f to 80304f2 Compare March 14, 2024 13:34
@phil-davis
Copy link
Contributor

Various acceptance tests for federated sharing are failing with:

Failed step: And user "Alice" has added the public share created from server "REMOTE" using the sharing API
        Sharing::userHasAddedPublicShareCreatedByUser
        Failed to save public share.
        'Remote is unreachable'
        Failed asserting that 'error' is not equal to 'error'.

Example: https://drone.owncloud.com/owncloud/core/39820/96/19

Maybe the test environment is not so good, or maybe the new code is too enthusiastic about deciding that the remote is not reachable. I will have a look...

@DeepDiver1975
Copy link
Member Author

@phil-davis any idea yet what is going on here? THX a lot

@phil-davis phil-davis force-pushed the fix/sharing-external-unreachable-hosts branch from 80304f2 to 5ecb099 Compare April 17, 2024 08:30
Comment on lines 78 to 84
$resp = $externalManager->testRemoteUrl(\OC::$server->getHTTPClientService(), $remote);
if ($resp === false) {
\OCP\JSON::error(['data' => ['message' => $l->t('Remote is unreachable')]]);
exit();
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The failing tests are all scenarios where a user tries to add a public link share.
The "federated" share is actually a public link.
That comes through this code.

Variable $remote at this point has the scheme http:// on the front.
But testRemoteUrl expects a URL without the scheme on the front.

I hacked the code like this:

$x = preg_replace("(^https?://)", "", $remote);
$resp = $externalManager->testRemoteUrl(\OC::$server->getHTTPClientService(), $x);
if ($resp === false) {
	\OCP\JSON::error(['data' => ['message' => $l->t("Remote '$x' is unreachable")]]);
	exit();
}

Now I run a test scenario:

make test-acceptance-api BEHAT_FEATURE=tests/acceptance/features/apiFederationToRoot1/savePublicShare.feature:22
...
Feature: Save public shares created by oC users

  Background:                                                                            # /home/phil/git/owncloud/core/tests/acceptance/features/apiFederationToRoot1/savePublicShare.feature:4
    Given using server "LOCAL"                                                           # FeatureContext::usingServer()
    And user "Alice" has been created with default attributes and without skeleton files # FeatureContext::userHasBeenCreatedWithDefaultAttributesAndWithoutSkeletonFiles()

  Scenario: Mount public share and then delete (local server share)                               # /home/phil/git/owncloud/core/tests/acceptance/features/apiFederationToRoot1/savePublicShare.feature:22
    Given user "Brian" has been created with default attributes and without skeleton files        # FeatureContext::userHasBeenCreatedWithDefaultAttributesAndWithoutSkeletonFiles()
    And user "Alice" has created folder "/PARENT"                                                 # FeatureContext::userHasCreatedFolder()
    And user "Alice" has created a public link share with settings                                # FeatureContext::userHasCreatedAPublicLinkShareWithSettings()
      | path        | /PARENT |
      | permissions | read    |
    And user "Brian" has added the public share created from server "LOCAL" using the sharing API # FeatureContext::userHasAddedPublicShareCreatedByUser()
      Sharing::userHasAddedPublicShareCreatedByUser
      Failed to save public share.
      'Remote '172.17.0.1:8080' is unreachable'
      Failed asserting that 'error' is not equal to 'error'.

But it still complains that '172.17.0.1:8080` is not reachable - that is my test installation which is running fine.

I wonder what is the next piece of this puzzle?

@phil-davis
Copy link
Contributor

The failing test scenarios are the ones about Brian adding a public share.

runsh: Total 69 scenarios (57 passed, 12 failed)
runsh: Exit code of main run: 1
runsh: Total unexpected failed scenarios throughout the test run:
apiFederationToRoot1/savePublicShare.feature:9
apiFederationToRoot1/savePublicShare.feature:22
apiFederationToRoot1/savePublicShare.feature:49
apiFederationToRoot1/savePublicShare.feature:50
apiFederationToRoot1/savePublicShare.feature:67
apiFederationToRoot1/savePublicShare.feature:68
apiFederationToRoot1/savePublicShare.feature:71
apiFederationToRoot1/savePublicShare.feature:86
apiFederationToRoot1/savePublicShare.feature:120
apiFederationToRoot1/savePublicShare.feature:121
apiFederationToRoot1/savePublicShare.feature:140
apiFederationToRoot1/savePublicShare.feature:141


runsh: Total 63 scenarios (53 passed, 10 failed)
runsh: Exit code of main run: 1
runsh: Total unexpected failed scenarios throughout the test run:
apiFederationToShares1/savePublicShare.feature:10
apiFederationToShares1/savePublicShare.feature:23
apiFederationToShares1/savePublicShare.feature:35
apiFederationToShares1/savePublicShare.feature:63
apiFederationToShares1/savePublicShare.feature:64
apiFederationToShares1/savePublicShare.feature:67
apiFederationToShares1/savePublicShare.feature:82
apiFederationToShares1/savePublicShare.feature:96
apiFederationToShares1/savePublicShare.feature:130
apiFederationToShares1/savePublicShare.feature:131

The usual case of users adding a federated share that is an ordinary file share on another server, that works.

@phil-davis
Copy link
Contributor

If one of the calls in testRemoteUrl to testUrl throws an exception, then the try-catch block finishes at that point, and the other calls to testUrl do not happen.

If I separate out each testUrl call into its own try-catch block, then I see that every testUrl call is getting ConnectException thrown.

I can just have it do one testUrl call:

	public function testRemoteUrl(IClientService $clientService, string $remote) {
		// cut query and|or anchor part off
		$remote = \strtok($remote, '?#');
		try {
			if ($this->testUrl($clientService, 'http://' . $remote . '/status.php', true)) {
				return 'http';
			}
		} catch (ConnectException $e) {
			// noop
		}

		return false;
	}

And that still gets ConnectException

But I can manually do:

curl http://172.17.0.1:8080/status.php
{"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"10.14.1.0","versionstring":"10.14.1 prealpha","edition":"Enterprise","productname":"ownCloud","product":"ownCloud"}

So the status.php endpoint is actually working.

So I suppose that there is some problem with how testUrl is setting up to HTTP client and doing the get request.

@DeepDiver1975
Copy link
Member Author

@phil-davis THX a lot for the analysis! Much appreciated!

@DeepDiver1975 DeepDiver1975 force-pushed the fix/sharing-external-unreachable-hosts branch 2 times, most recently from 8a95a20 to 5410eab Compare April 18, 2024 07:24
@DeepDiver1975
Copy link
Member Author

DeepDiver1975 commented Apr 18, 2024

{
  "reqId": "XSMwTG4Mm0JBu2okCpJC",
  "level": 3,
  "time": "2024-04-18T07:53:46+00:00",
  "remoteAddr": "192.168.8.3",
  "user": "Brian",
  "app": "no app in context",
  "method": "POST",
  "url": "/apps/files_sharing/external",
  "message": "Remote server not reachable: {\"Exception\":\"GuzzleHttp\\\\Exception\\\\ConnectException\",\"Message\":\"cURL error 35: error:1408F10B:SSL routines:ssl3_get_record:wrong version number (see https:\\/\\/curl.haxx.se\\/libcurl\\/c\\/libcurl-errors.html) for https:\\/\\/server\\/ocs-provider\\/\",\"Code\":0,\"Trace\":\"#0 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Handler\\/CurlFactory.php(158): GuzzleHttp\\\\Handler\\\\CurlFactory::createRejection()\\n#1 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Handler\\/CurlFactory.php(110): GuzzleHttp\\\\Handler\\\\CurlFactory::finishError()\\n#2 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Handler\\/CurlHandler.php(47): GuzzleHttp\\\\Handler\\\\CurlFactory::finish()\\n#3 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Handler\\/Proxy.php(28): GuzzleHttp\\\\Handler\\\\CurlHandler->__invoke()\\n#4 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Handler\\/Proxy.php(48): GuzzleHttp\\\\Handler\\\\Proxy::GuzzleHttp\\\\Handler\\\\{closure}(*** sensitive parameters replaced ***)\\n#5 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/PrepareBodyMiddleware.php(35): GuzzleHttp\\\\Handler\\\\Proxy::GuzzleHttp\\\\Handler\\\\{closure}(*** sensitive parameters replaced ***)\\n#6 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Middleware.php(31): GuzzleHttp\\\\PrepareBodyMiddleware->__invoke()\\n#7 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/RedirectMiddleware.php(71): GuzzleHttp\\\\Middleware::GuzzleHttp\\\\{closure}(*** sensitive parameters replaced ***)\\n#8 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Middleware.php(66): GuzzleHttp\\\\RedirectMiddleware->__invoke()\\n#9 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/HandlerStack.php(75): GuzzleHttp\\\\Middleware::GuzzleHttp\\\\{closure}(*** sensitive parameters replaced ***)\\n#10 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Client.php(333): GuzzleHttp\\\\HandlerStack->__invoke()\\n#11 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Client.php(169): GuzzleHttp\\\\Client->transfer()\\n#12 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Client.php(189): GuzzleHttp\\\\Client->requestAsync()\\n#13 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/ClientTrait.php(44): GuzzleHttp\\\\Client->request()\\n#14 \\/drone\\/src\\/lib\\/private\\/Http\\/Client\\/Client.php(184): GuzzleHttp\\\\Client->get()\\n#15 \\/drone\\/src\\/apps\\/files_sharing\\/lib\\/External\\/Manager.php(553): OC\\\\Http\\\\Client\\\\Client->get()\\n#16 \\/drone\\/src\\/apps\\/files_sharing\\/lib\\/External\\/Manager.php(578): OCA\\\\Files_Sharing\\\\External\\\\Manager->testUrl()\\n#17 \\/drone\\/src\\/apps\\/files_sharing\\/ajax\\/external.php(78): OCA\\\\Files_Sharing\\\\External\\\\Manager->testRemoteUrl()\\n#18 \\/drone\\/src\\/lib\\/private\\/Route\\/Route.php(155): require_once('\\/drone\\/src\\/apps...')\\n#19 \\/drone\\/src\\/lib\\/private\\/Route\\/Router.php(344): OC\\\\Route\\\\Route->OC\\\\Route\\\\{closure}(*** sensitive parameters replaced ***)\\n#20 \\/drone\\/src\\/lib\\/kernel.php(925): OC\\\\Route\\\\Router->match()\\n#21 \\/drone\\/src\\/index.php(32): OC::handleRequest()\\n#22 {main}\",\"File\":\"\\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Handler\\/CurlFactory.php\",\"Line\":210}"
}

SSL issue

emote server not reachable: {\"Exception\":\"GuzzleHttp\\\\Exception\\\\ConnectException\",\"Message\":\"cURL error 35: error:1408F10B:SSL routines:ssl3_get_record:wrong version number (see https:\\/\\/curl.haxx.se\\/libcurl\\/c\\/libcurl-errors.html) for https:\\/\\/server\\/ocs-provider\\/\",\"Code\":0,\"Trace\":\"#0 \\/drone\\/src\\/lib\\/composer\\/guzzlehttp\\/guzzle\\/src\\/Handler\\/CurlFactory.php(158):

@DeepDiver1975 DeepDiver1975 force-pushed the fix/sharing-external-unreachable-hosts branch 2 times, most recently from f14cf07 to 07bab99 Compare April 22, 2024 13:31
@DeepDiver1975 DeepDiver1975 force-pushed the fix/sharing-external-unreachable-hosts branch 3 times, most recently from 28360d9 to 7095340 Compare April 22, 2024 15:33
@DeepDiver1975 DeepDiver1975 force-pushed the fix/sharing-external-unreachable-hosts branch from 7095340 to 4fe718e Compare April 22, 2024 15:54
@DeepDiver1975
Copy link
Member Author

@phil-davis mind another review? THX

@phil-davis phil-davis mentioned this pull request Apr 23, 2024
11 tasks
Copy link
Contributor

@phil-davis phil-davis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the remote system is not listening on port 80, but some other port, then the checks do not work - they get directed to the "default" port 80, but need to be directed to the correct port.

See the last commit of PR #41237 - with that extra code the tests pass for me in my dev setup that has 2 servers running on localhost port 8080 and 8180 on my laptop.

Comment on lines +611 to +614
if ($this->testUrl($clientService, $schema . $remote . '/status.php', false, true)) {
\OC::$server->getLogger()->error("Remote $schema$remote/status.php reachable");
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ($this->testUrl($clientService, $schema . $remote . '/status.php', false, true)) {
\OC::$server->getLogger()->error("Remote $schema$remote/status.php reachable");
return true;
}
if ($this->testUrl($clientService, $schema . $remote . '/status.php', false, true)) {
return true;
}
\OC::$server->getLogger()->error("Remote $schema$remote/status.php not reachable");

If the host:port of the remote is completely down, then testUrl is returning false, but I get nothing in the log about it.
Maybe move the error logging to the unhappy path, and don't log when reachable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx for catching this 🙏

@phil-davis
Copy link
Contributor

Note: for my local testing, I found that this scenario was good to run:

make test-acceptance-api BEHAT_FEATURE=tests/acceptance/features/apiFederationToRoot1/savePublicShare.feature:71

That scenario uses both local and remote (federated) servers and a public link.
Just putting this here so that I find it easily if I need it again.

@sonarqubecloud
Copy link

@DeepDiver1975 DeepDiver1975 merged commit 7b4b2f0 into master Apr 26, 2024
@delete-merged-branch delete-merged-branch bot deleted the fix/sharing-external-unreachable-hosts branch April 26, 2024 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants