Skip to content

Commit

Permalink
better error handling, load urls correctly now without http client
Browse files Browse the repository at this point in the history
  • Loading branch information
cweiske committed May 2, 2013
1 parent bc81b15 commit d6553b6
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 15 deletions.
3 changes: 3 additions & 0 deletions examples/webfinger-cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
get_include_path() . PATH_SEPARATOR . __DIR__ . '/../src/'
);
}
set_include_path(
'/home/cweiske/Dev/pear/git-packages/XML_XRD/src/' . PATH_SEPARATOR . get_include_path()
);
require_once 'Net/WebFinger.php';


Expand Down
74 changes: 60 additions & 14 deletions src/Net/WebFinger.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
class Net_WebFinger
{
/**
* Retry with HTTP if the HTTPS request fails.
* Retry with HTTP if the HTTPS webfinger request fails.
* This is not allowed by the webfinger specification, but may be
* helpful during development.
*
Expand Down Expand Up @@ -117,7 +117,17 @@ public function finger($identifier)
return $hostMeta;
}

return $this->loadLrdd($identifier, $host, $hostMeta);
$react = $this->loadLrdd($identifier, $host, $hostMeta);
if ($react->error
&& $react->error->getCode() == Net_WebFinger_Error::NO_LRDD
) {
$react->error = new Net_WebFinger_Error(
'No webfinger data found',
Net_WebFinger_Error::NOTHING,
$react->error
);
}
return $react;
}

/**
Expand Down Expand Up @@ -146,14 +156,12 @@ protected function loadWebfinger($identifier, $host)
$react = new Net_WebFinger_Reaction();
$userUrl = 'http://' . substr($userUrl, 8);
$this->loadXrd($react, $userUrl);
$react->secure = false;
}
if ($react->error !== null) {
return $react;
}

if (!$this->isHttps($userUrl)) {
$react->secure = false;
}
$this->verifyDescribes($react, $account);

return $react;
Expand Down Expand Up @@ -276,12 +284,7 @@ protected function loadLrdd(
) {
//copy certain links from hostMeta to lrdd
$react = new Net_WebFinger_Reaction();
foreach ($hostMeta->links as $link) {
if ($link->rel == 'http://specs.openid.net/auth/2.0/provider') {
$react->links[] = $link;
}
}
$react->secure = $hostMeta->secure;
$this->mergeHostMeta($react, $hostMeta);

$link = $hostMeta->get('lrdd', 'application/xrd+xml');
if ($link === null || !$link->template) {
Expand All @@ -304,6 +307,11 @@ protected function loadLrdd(
$res = $this->loadXrd($react, $userUrl);
}
if (!$res) {
$react->error = new Net_WebFinger_Error(
'LRDD file not found',
Net_WebFinger_Error::NO_LRDD,
$react->error
);
return $react;
}

Expand All @@ -315,6 +323,17 @@ protected function loadLrdd(
return $react;
}

protected function mergeHostMeta(
Net_WebFinger_Reaction $react, Net_WebFinger_Reaction $hostMeta
) {
foreach ($hostMeta->links as $link) {
if ($link->rel == 'http://specs.openid.net/auth/2.0/provider') {
$react->links[] = $link;
}
}
$react->secure = $hostMeta->secure;
}

protected function verifyDescribes(Net_WebFinger_Reaction $react, $account)
{
if (!$react->describes($account)) {
Expand Down Expand Up @@ -352,17 +371,44 @@ protected function isHttps($url)
protected function loadXrd(Net_WebFinger_Reaction $react, $url)
{
try {
$react->error = null;
if ($this->httpClient !== null) {
$this->httpClient->setUrl($url);
$this->httpClient->setHeader(
'accept',
'application/jrd+json, application/xrd+xml;q=0.9',
true
);
$react->loadString($this->httpClient->send()->getBody());
$res = $this->httpClient->send();
$code = $res->getStatus();
if (intval($code / 100) !== 2) {
throw new Net_WebFinger_Error(
'Error loading XRD file: ' . $res->getStatus()
. ' ' . $res->getReasonPhrase(),
Net_WebFinger_Error::NOT_FOUND
);
}
$react->loadString($res->getBody());
} else {
//FIXME: set accept header with file_get_contents, too
$react->loadFile($url);
$context = stream_context_create(
array(
'http' => array(
'user_agent' => 'PEAR Net_WebFinger',
'header' => 'accept: application/jrd+json, application/xrd+xml;q=0.9',
)
)
);
$content = @file_get_contents($url, false, $context);
if ($content === false) {
$msg = 'Error loading XRD file';
if (isset($http_response_header)) {
$msg .= ': ' . $http_response_header[0];
};
throw new Net_WebFinger_Error(
$msg, Net_WebFinger_Error::NOT_FOUND
);
}
$react->loadString($content);
}
return true;
} catch (Exception $e) {
Expand Down
17 changes: 16 additions & 1 deletion src/Net/WebFinger/Error.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,25 @@ class Net_WebFinger_Error extends Exception
*/
const NO_LRDD_LINK = 2342011;

/**
* The LRDD file could not be found
*/
const NO_LRDD = 2342012;

/**
* XRD file does not describe() the requested account
*/
const DESCRIBE = 2342012;
const DESCRIBE = 2342013;

/**
* XRD could file could not be found
*/
const NOT_FOUND = 2342014;

/**
* No webfinger data found
*/
const NOTHING = 2342015;
}

?>
101 changes: 101 additions & 0 deletions tests/Net/WebFingerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ public function testFingerFetchesWebfingerFirst()
$this->assertDescribes('acct:user@example.org', $react);
}

public function testFingerWebfingerFallbackHttp()
{
$this->addHttpResponse(
new HTTP_Request2_Exception('No webfinger for you.')
)->addHttpResponse(
$this->getWebfinger()
);

$this->wf->fallbackToHttp = true;
$react = $this->wf->finger('user@example.org');

$this->assertUrlList(
'https://example.org/.well-known/webfinger?resource=acct%3Auser%40example.org',
'http://example.org/.well-known/webfinger?resource=acct%3Auser%40example.org'
);
$this->assertDescribes('acct:user@example.org', $react);
}

public function testFingerFetchesHostMetaSslBeforeNonSsl()
{
$this->addHttpResponse(
Expand Down Expand Up @@ -68,6 +86,26 @@ public function testFingerLrdd()
$this->assertDescribes('acct:user@example.org', $react);
}

public function testFingerLrddOpenIdFromHostMeta()
{
$this->addHttpResponse(
new HTTP_Request2_Exception('No webfinger for you.')
)
->addHttpResponse($this->getHostMetaOpenId())
->addHttpResponse($this->getLrddEmpty());
$react = $this->wf->finger('user@example.org');

$this->assertUrlList(
'https://example.org/.well-known/webfinger?resource=acct%3Auser%40example.org',
'https://example.org/.well-known/host-meta',
'https://example.org/lrdd?acct=acct%3Auser%40example.org'
);

$this->assertNoError($react);
$this->assertDescribes('acct:user@example.org', $react);
$this->assertEquals('http://id.example.org/', $react->openid);
}

public function testFingerLrddFallbackHttp()
{
$this->addHttpResponse(
Expand Down Expand Up @@ -124,6 +162,33 @@ public function testFingerSecurityLrddAllHttps()
$this->assertTrue($react->secure);
}

public function testFingerNoLrddFile()
{
$this->addHttpResponse(
new HTTP_Request2_Exception('No webfinger for you.')
)
->addHttpResponse($this->getHostMeta());
$react = $this->wf->finger('user@example.org');

$this->assertNotNull($react->error);
$this->assertEquals(
'No webfinger data found',
$react->error->getMessage()
);

$this->assertNotNull($react->error->getPrevious());
$this->assertEquals(
'LRDD file not found',
$react->error->getPrevious()->getMessage()
);

$this->assertNotNull($react->error->getPrevious()->getPrevious());
$this->assertEquals(
'Error loading XRD file: 400 Bad Request',
$react->error->getPrevious()->getPrevious()->getMessage()
);
}

public function testFingerSecurityHostMetaHttp()
{
$this->addHttpResponse(
Expand Down Expand Up @@ -294,6 +359,25 @@ protected function getHostMeta()
);
}

protected function getHostMetaOpenId()
{
return implode(
"\r\n",
array(
'HTTP/1.1 200 OK',
'Content-Type: application/xrd+xml',
'Connection: close',
'',
'<?xml version="1.0"?>',
'<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">',
' <Subject>example.org</Subject>',
' <Link rel="lrdd" template="https://example.org/lrdd?acct={uri}" />',
' <Link rel="http://specs.openid.net/auth/2.0/provider" href="http://id.example.org/"/>',
'</XRD>'
)
);
}

protected function getHostMetaEmpty()
{
return implode(
Expand Down Expand Up @@ -328,5 +412,22 @@ protected function getLrdd()
)
);
}

protected function getLrddEmpty()
{
return implode(
"\r\n",
array(
'HTTP/1.1 200 OK',
'Content-Type: application/xrd+xml',
'Connection: close',
'',
'<?xml version="1.0"?>',
'<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">',
' <Subject>acct:user@example.org</Subject>',
'</XRD>'
)
);
}
}
?>

0 comments on commit d6553b6

Please sign in to comment.