Gestion des temps morts Soap en PHP

je travaille sur un projet où je vérifie des informations d'un utilisateur avec un service Web SOAP. Je m'occupe actuellement des erreurs en supposant que je reçois des réponses du service web, mais j'ai aussi besoin de gérer les cas limites d'un délai de service ou d'indisponibilité.

dans le cas d'un délai ou d'une indisponibilité du service, je dois prétendre que la demande a été acceptée (que le service Web a approuvé l'information), mais je ne suis pas clair sur les exceptions sont jetée.

un pseudo-code:

// $client is PHP's SoapClient class
try {
  $response = $client->SomeSoapRequest();
}
catch(SoapFault $e){
  // handle issues returned by the web service
}
catch(Exception $e){
  // handle PHP issues with the request
}

Ce que je n'arrive pas à trouver est:

  1. délais d'attente Sont un SoapFault? Si oui, quelle est la meilleure façon de faire la distinction entre une erreur de timeout et les problèmes de service web (comme une erreur de type, etc.)? J'ai trouvé une page qui mentionnait une erreur où le message était quelque chose à l'effet de "en-têtes de chargement D'erreur", mais n'a pas mentionné si c'était une faute de Soap.
  2. comment une indisponibilité du service peut-elle se produire? de se produire? Une exception PHP semble sensée (un SoapFault serait retourné du service web où l'indisponibilité serait un problème de socket ou similaire)?
  3. Existe-t-il un service existant (par exemple) sur lequel je peux tester un délai d'attente? La plupart des discussions sur le timeout semblent être liées à la prévention des timeouts en prolongeant le paramètre de timeout par défaut, ce qui n'est pas idéal dans cette situation.
32
demandé sur hakre 2009-05-07 18:59:40

8 réponses

1) en cas de timeout, PHP lance une exception SoapFault avec faultcode="HTTP" et faultstring="Error Fetching http headers".



2) à mon avis, la meilleure façon de faire la distinction entre une erreur de temporisation et les problèmes de service web est d'examiner le faultcode et faultstring membres de la Classe SoapFault.

En particulier, le faultcode l'élément est destiné à être utilisé par un logiciel pour fournir un mécanisme algorithmique permettant d'identifier la défaillance.

Comme vous pouvez aussi le lu dans un commentaire du manuel PHP, il n'y a pas de méthode pour lire le faultcode propriété, de sorte que vous devez y accéder directement (par ex. $e->faultcode), parce que le getCode() méthode ne fonctionne pas.

SOAP 1.1 Spec définit quatre valeurs possibles pour l' faultcode champ:

  • VersionMismatch: L'équipe de traitement a trouvé un namespace invalide pour L'enveloppe SOAP l'élément
  • must understand: un élément enfant immédiat de L'élément D'en-tête SOAP qui n'a pas été compris ou n'a pas été obéi par la partie de traitement contenait un attribut soap mustUnderstand avec une valeur de "1"
  • Client: la catégorie D'erreurs du Client indique que le message a été mal formé ou qu'il ne contenait pas l'information appropriée pour réussir. Par exemple, le message pourrait ne pas avoir l'authentification appropriée ou les informations de paiement. Il s'agit généralement d'une indication que le message ne devrait pas être résenté sans changement.
  • Serveur: la classe D'erreurs du serveur indique que le message n'a pas pu être traité pour des raisons qui ne sont pas directement attribuables au contenu du message lui-même, mais plutôt au traitement du message. Par exemple, le traitement pourrait inclure la communication avec un processeur en amont, qui n'a pas répondu. Le message peut réussir à un moment ultérieur dans temps.

en plus de ces codes, PHP utilise le HTTP code d'identification des erreurs se produisant au niveau du protocole (p. ex.: socket errors); par exemple, si vous cherchez add_soap_fault dans le ext/soap / php_http.c code source, vous pouvez voir si certains de ces types de défauts sont générés.

Par la recherche de l' add_soap_fault et soap_server_fault fonctions dans les fichiers source de L'extension SOAP de PHP, j'ai créé la liste suivante de PHP SoapFault exceptions:

HTTP
----
Unable to parse URL
Unknown protocol. Only http and https are allowed.
SSL support is not available in this build
Could not connect to host
Failed Sending HTTP SOAP request
Failed to create stream??
Error Fetching http headers
Error Fetching http body: No Content-Length: connection closed or chunked data
Redirection limit reached: aborting
Didn't recieve an xml document
Unknown Content-Encoding
Can't uncompress compressed response
Error build soap request


VersionMismatch
---------------
Wrong Version


Client
------
A SOAP 1.2 envelope can contain only Header and Body
A SOAP Body element cannot have non Namespace qualified attributes
A SOAP Envelope element cannot have non Namespace qualified attributes
A SOAP Header element cannot have non Namespace qualified attributes
Bad Request
Body must be present in a SOAP envelope
Can't find response data
DTD are not supported by SOAP
encodingStyle cannot be specified on the Body
encodingStyle cannot be specified on the Envelope
encodingStyle cannot be specified on the Header
Error cannot find parameter
Error could not find "location" property
Error finding "uri" property
looks like we got "Body" with several functions call
looks like we got "Body" without function call
looks like we got no XML document
looks like we got XML without "Envelope" element
Missing parameter
mustUnderstand value is not boolean
SoapClient::__doRequest() failed
SoapClient::__doRequest() returned non string value
Unknown Data Encoding Style
Unknown Error
DataEncodingUnknown


MustUnderstand
--------------
Header not understood


Server
------
Couldn't find WSDL
DTD are not supported by SOAP
Unknown SOAP version
WSDL generation is not supported yet

3) pour simuler la condition de timeout, essayez avec le code suivant:

soapclient.php

<?php

ini_set('default_socket_timeout', 10);

$client = new SoapClient(null, 
  array(
    'location' => "http://localhost/soapserver.php",
    'uri'      => "http://localhost/soapserver.php",
    'trace'    => 1
  )
);

try {
    echo $return = $client->__soapCall("add",array(41, 51));
} catch (SoapFault $e) {
    echo "<pre>SoapFault: ".print_r($e, true)."</pre>\n";
    //echo "<pre>faultcode: '".$e->faultcode."'</pre>";
    //echo "<pre>faultstring: '".$e->getMessage()."'</pre>";
}

?>

soapserver.php

<?php

function add($a, $b) {
  return $a + $b;
}

sleep(20);

$soap = new SoapServer(null, array('uri' => 'http://localhost/soapserver.php'));
$soap->addFunction("add");
$soap->handle();

?>

Notez le sleep dans le SoapServer.php script avec un temps (20) plus long que le temps (10) spécifié pour le default_socket_timeout paramètre SoapClient.php script.

Si vous voulez simuler une indisponibilité de service, vous pouvez par exemple changer le location protocole httphttps dans le soapclient.php script, en supposant que votre serveur web n'est pas configuré pour SSL; en faisant cela, PHP devrait lancer un SoapFault "ne peut pas se connecter à l'hôte".

30
répondu Greg K 2014-10-03 12:43:59

Ressemble default_socket_timeout n'est pas pris en compte lors des appels SOAP sur HTTPS:

Bogue ouvert au moment de la rédaction. Comme un commentaire sur le blog de Robert Ludwick référencé supprimé réponse temporisation PHP Appels Soap (21 Oct 2009; Publiés par Robert F. Ludwick) souligne, le contournement du poteau discute (en ignorant SoapClient::__doRequest() avec une requête curl) fonctionne aussi autour de ce bug.

un autre bug lié est:


Le code mentionné dans le billet de blog a subi quelques modifications et peut être trouvé dans c'est la dernière forme avec le soutien de L'authentification HTTP ici sur Github:

dans tous les cas, la solution de contournement ne devrait plus être nécessaire car ce problème a été corrigé dans L'extension PHP SOAPClient.

7
répondu tobych 2015-04-12 15:16:53

Pour faire face aux délais d'attente dans le service

$client = new SoapClient($wsdl, array("connection_timeout"=>10));

// SET SOCKET TIMEOUT
if(defined('RESPONSE_TIMEOUT') &&  RESPONSE_TIMEOUT != '') {
 ini_set('default_socket_timeout', RESPONSE_TIMEOUT);
}
3
répondu ToughPal 2011-01-31 18:07:57

à Partir de mon expérience, si $e->getMessage "Erreur de Récupérer les en-têtes http", vous avez affaire à un délai d'attente réseau.

Si $e->getMessage est quelque chose comme" ne peut pas se connecter à l'hôte", le service que vous essayez d'atteindre est en baisse.

puis il y a "On dirait qu'on n'a pas de document XML", ce qui est plus cryptique et peut signifier différentes choses.

2
répondu wosis 2011-01-31 18:07:16

j'ai utilisé deux facteurs pour obtenir mon extention SoapClient pour lancer une belle exception. Le message et le temps de retour de la demande. Je pense que le message d'erreur "Error Fetching http headers" peut aussi se produire dans d'autres cas, donc la vérification de l'heure.

le code suivant devrait être à peu près correct

class SoapClientWithTimeout extends SoapClient {
    public function __soapCall ($params, ---) {
        $time_start = microtime(true);
        try {
            $result = parent::__soapCall ($params, ---);
        }
        catch (Exception $e) {
            $time_request = (microtime(true)-$time_start);
            if(
                $e->getMessage() == 'Error Fetching http headers' &&
                ini_get('default_socket_timeout') < $time_request
            ) {
                throw new SoapTimeoutException(
                    'Soap request most likly timed out.'.
                    ' It took '.$time_request.
                    ' and the limit is '.ini_get('default_socket_timeout')
                );
            }

            // E: Not a timeout, let's rethrow the original exception
            throw $e;
        }

        // All good, no exception from the service or PHP
        return $result;
    }
}

class SoapTimeoutException extends Exception {}

j'utilise alors SoapClientWithTimeout

$client = new SoapClientWithTimeout();
try {
    $response = $client->SomeSoapRequest();
    var_dump($response);
}
catch(SoapTimeoutException $e){
    echo 'We experienced a timeout! '. $e->getMessage();
}
catch(Exception $e) {
    echo 'Exception: '.$e->getMessage();
}

pour déboguer votre chronométrage de service. Ajoutez la ligne suivante avant d'appeler la service

ini_set('default_socket_timeout', 1);
1
répondu HNygard 2012-04-27 21:20:51

Simplement le réglage de l' default_socket_timeout globalement via l'ini peut ne pas faire ce que vous voulez. Cela affecterait les requêtes SOAP, mais affecterait également les autres connexions sortantes, y compris les connexions DB. À la place, outrepassez la méthode __doRequest() de SoapClient pour faire vous-même la connexion HTTP. Vous pouvez alors définir votre propre timeout sur la socket, le détecter, et lancer des exceptions que vous pouvez piéger et gérer.

class SoapClientWithTimeout extends SoapClient {

    public function __construct ($wsdl, $options = null) {
        if (!$options) $options = [];

        $this->_connectionTimeout =
            @$options['connection_timeout']
            ?: ini_get ('default_socket_timeout');
        $this->_socketTimeout =
            @$options['socket_timeout']
            ?: ini_get ('default_socket_timeout');
        unset ($options['socket_timeout']);

        parent::__construct($wsdl, $options);
    }

    /**
     * Override parent __doRequest to add a timeout.
     */
    public function __doRequest (
        $request, $location, $action, $version, $one_way = 0
    ) {
        // Extract host, port, and scheme.
        $url_parts = parse_url ($location);
        $host = $url_parts['host'];
        $port =
            @$url_parts['port']
            ?: ($url_parts['scheme'] == 'https' ? 443 : 80);
        $length = strlen ($request);

        // Form the HTTP SOAP request.
        $http_req = "POST $location HTTP/1.0\r\n";
        $http_req .= "Host: $host\r\n";
        $http_req .= "SoapAction: $action\r\n";
        $http_req .= "Content-Type: text/xml; charset=utf-8\r\n";
        $http_req .= "Content-Length: $length\r\n";
        $http_req .= "\r\n";
        $http_req .= $request;

        // Need to tell fsockopen to use SSL when requested.
        if ($url_parts['scheme'] == 'https')
            $host = 'ssl://'.$host;

        // Open the connection.
        $socket = @fsockopen (
            $host, $port, $errno, $errstr, $this->_connectionTimeout
        );
        if (!$socket)
            throw new SoapFault (
                'Client',
                "Failed to connect to SOAP server ($location): $errstr"
            );

        // Send the request.
        stream_set_timeout ($socket, $this->_socketTimeout);
        fwrite ($socket, $http_req);

        // Read the response.
        $http_response = stream_get_contents ($socket);

        // Close the socket and throw an exception if we timed out.
        $info = stream_get_meta_data ($socket);
        fclose ($socket);
        if ($info['timed_out'])
            throw new SoapFault (
                'Client',
                "HTTP timeout contacting $location"
            );

        // Extract the XML from the HTTP response and return it.
        $response = preg_replace (
            '/
                \A       # Start of string
                .*?      # Match any number of characters (as few as possible)
                ^        # Start of line
                \r       # Carriage Return
                $        # End of line
             /smx',
            '', $http_response
        );
        return $response;
    }

}
0
répondu Derek 2015-02-19 22:23:08

je suppose que je suis un peu en retard, mais au cas où quelqu'un est encore à la recherche d'une solution pour les temps morts dans le client soap php - voici ce qui a fonctionné pour moi:

remplacer fondamentalement PHP SoapClient par cURL avec set timeout. Il suffit de garder à l'esprit que, parfois, WS attend des actions spécifiées dans L'en-tête HTTP. La solution originale postée sur ce site web ne comprend pas cela (vérifier commentaire.)

0
répondu Maciej Jaśniaczyk 2015-04-12 05:53:31

utilisez simplement "stream_context" pour définir le paramètre timeout aussi pour le chargement de WSDL (vous devez définir les options $de SoapClient ['connection_timeout'] avant):

class SoapClient2 extends SoapClient
{
  public function __construct($wsdl, $options=null)
  {
    if(isset($options['connection_timeout']))
    {
      $s_options = array(
          'http' => array(
              'timeout' => $options['connection_timeout']
              )
          );
      $options['stream_context'] = stream_context_create($s_options);
    }
    parent::__construct($wsdl, $options);
  }
}
0
répondu sh3b4ng 2015-04-22 12:32:53