繁体   English   中英

使用cURL访问REST API时发生无限循环,是什么原因引起的?

[英]Infinite loop when using cURL to access a REST API, what is causing it?

我正在尝试使用cURL编写连接器以连接到REST API。

用户要做的第一步是使用createSession()创建会话。 这将使用用户名和密码将POST调用发送到API。 API将以sessionId,cookie值和几个自定义标头进行响应。

该会话仅在每个有效请求后3分钟内有效。 如果我在会话期满后发出请求,我将收到http error code 401 ,该http error code 401表示用户由于sessionId无效或超时而未被授权发出请求。

我不想让用户手动再次登录,而是想在出现错误401时通过调用createSession()方法来自动重新连接。之所以需要这样做是因为sessionId被保存在客户端cookie中,因此客户端不知道会话是否已过期或处于活动状态。 我的代码将尝试使用保存在cookie中的sessionId激活或过期的sessionId来调用方法。

只要会话仍处于活动状态,API就会在每次请求时3分钟更新会话。 仅在用户3分钟未提出请求时,我才需要重新连接。

我遇到的问题是,当我尝试重新连接时,我陷入了一个无限循环,无法弄清楚如何阻止它。

这是我的代码

<?php namespace API;

/**
 * ICWS API
 *
 * @package ICWS
 */
class ICWS {

    private $_myAppName = 'ICWS API connector';
    private $_authenticationType = 'Basic'; //Not used yet
    private $_languageID = 'en-US';
    private $_protocol = 'http';
    private $_sessionIdKey = 'sessionId';
    private $_interactionIdKey = 'interactionIdKey';
    private $_maxLoginAttempts = 3;
    private $_loginAttempts = 0;
    private $_debug = false;

    //No need to edit beyond this line
    private $_isSubscribledToQueue = false;
    private $_alternateHostList = array();
    private $_interactionId = 0;

    private $_queueType = 1;
    private $_userID;
    private $_password;
    private $_workstation;
    private $_queueName;
    private $_cainfo;
    private $_baseURL;
    private $_csrfToken;
    private $_sessionId;
    private $_ININ_ICWS_CSRF_Token;
    private $_Location;
    private $_subscriptionId;
    private $_curlHeader;
    private $_requestFile;

    public function __construct($config)
    {

        //Make sure cURL is enabled on the server
        if(!is_callable('curl_init')){
            throw new ApiException('cURL is disabled on this server. Before making API calls cURL extension must be enabled.');
        }

        //Make sure all required config are set
        if(    !isset($config['host']) || empty($config['host'])
            || !isset($config['port']) || empty($config['port'])
            || !isset($config['userID']) || empty($config['userID'])
            || !isset($config['password']) || empty($config['password'])
            || !isset($config['workstation']) || empty($config['workstation'])
        ){
            throw new ApiException('Host, Port, userID, password, workstation are required!');
        }

        $this->_userID = $config['userID'];
        $this->_password = $config['password'];
        $this->_workstation = $config['workstation'];

        //override the default queueType
        if( isset($config['queueType']) && !empty($config['queueType']) ){
            $this->_queueType = $config['queueType'];
        }       

        //override the default queueName
        if( isset($config['queueName']) && !empty($config['queueName']) ){
            $this->_queueName = $config['queueName'];
        }

        //override the default appName
        if( isset($config['appName']) && !empty($config['appName']) ){
            $this->_myAppName = $config['appName'];
        }

        //override the default session Key
        if( isset($config['sessionKey']) && !empty($config['sessionKey']) ){
            $this->_sessionKey = $config['sessionKey'];
        }

        //override the default protocol
        if( isset($config['isSecured']) && $config['isSecured'] == true){

            if(!isset($config['cainfo']) || empty($config['cainfo'])){
                throw new ApiException('To enable SSL you must provide CA Info file (.cert)');
            } else {
                $this->_protocol = 'https';
                $this->cainfo = $config['cainfo'];
            }
        }

        //override the default server Language
        if( isset($config['languageID']) && !empty($config['languageID']) ){
            $this->_languageID = $config['languageID'];
        }

        //override the default debug mode
        if( isset($config['debug']) && !empty($config['debug']) ){
            $this->_debug = $config['debug'];
        }

        //override the default authentication type
        if( isset($config['authenticationType']) && !empty($config['authenticationType']) ){
            $this->_authenticationType = $config['authenticationType'];
        }

        //set the sessionId if it already exists
        if( isset( $_COOKIE[$this->_sessionIdKey] ) && !empty( $_COOKIE[$this->_sessionIdKey] )){
            $this->_sessionId = $_COOKIE[$this->_sessionIdKey];
        }

        //set the _interactionIdKey if it already exists
        if( isset( $_COOKIE[$this->_interactionIdKey] ) && !empty( $_COOKIE[$this->_interactionIdKey] )){
            $this->_interactionId = $this->_bigint($_COOKIE[$this->_interactionIdKey]);

        }

        if(isset($_COOKIE['ININ-ICWS-CSRF-Token']) && !empty($_COOKIE['ININ-ICWS-CSRF-Token'])){
            $this->_ININ_ICWS_CSRF_Token = $_COOKIE['ININ-ICWS-CSRF-Token'];
        }


        $this->_baseURL = $this->_protocol . '://' . $config['host'] . ':' . $config['port'] . '/icws/';
        $this->_subscriptionId = $this->_userID;        
    }

    /**
    * Authentication the user and generated a sessionId
    *
    * @param string $userID
    * @param string $password
    * @param boolean $forceNewSession
    * @catch exception
    * @return void
    */  
    public function createSession($forceNewSession = false){

        if( !empty($this->_sessionId) && ! $forceNewSession ){
            return;
        }

        if($forceNewSession){
            $this->destroySession();
        }

        $this->_requestFile = 'connection';
        $type = 'urn:inin.com:connection:icAuthConnectionRequestSettings';

        $data = array('__type' => $type,
                      'applicationName' => $this->_myAppName,
                      'userID' => $this->_userID,
                      'password' => $this->_password);

        $this->_curlHeader = array('Accept-Language: ' . $this->_languageID,
                                   'Content-Type: application/json');
        $httpCode = 0;

        try {
            $data = $this->_processRequest('POST', 'connection', $data, $httpCode, false);

            if($this->_debug){
                new showVar($data, false, 'HTTP Code: ' . $httpCode);
            }

            $this->_csrfToken = $data['csrfToken'];
            $this->_sessionId = $data['sessionId'];
            $this->_alternateHostList = $data['alternateHostList'];

            if(!empty($this->_sessionId)){
                setCookie($this->_sessionIdKey, $this->_sessionId);
                $this->_loginAttempts = 0;
            }

        } catch (\Exception  $e){
            $this->_displayError($e);
        }
    }

    /**
    * Destroy the IC session
    *
    * @return void
    */      
    public function destroySession(){

        //destroy the sessionId
        $this->_sessionId = NULL;
        $this->_destroy($this->_sessionIdKey);

        //destroy the sessionId
        $this->_interactionIdKey = 0;
        $this->_destroy($this->_interactionIdKey);  

        //destroy the CSRF-Token
        $this->_ININ_ICWS_CSRF_Token = NULL;
        $this->_destroy('ININ-ICWS-CSRF-Token');    

    }   

    /**
    * Calls any Method after a session is created
    *
    * @param string $method GET/POST/PUT
    * @param string $uri 
    * @param array $data
    * @catch exception
    * @return array or false
    */
    private function _sendRequest($method, $uri, $data = false, &$httpCode = 0){

        if( !$this->_sessionId ){
            return false;
        }

        $uri = $this->_sessionId . '/' . $uri;
        $return = false;
        //,'Cookie: ' . $this->_ININ_ICWS_Cookie
        $this->_curlHeader = array('ININ-ICWS-CSRF-Token: ' . $this->_ININ_ICWS_CSRF_Token,
                                   'ININ-ICWS-Session-ID: ' . $this->_sessionId,
                                   'Content-Type: application/json');

        try {
            $return = $this->_processRequest($method, $uri, $data, $httpCode);
        } catch (\Exception  $e){
            $this->_displayError($e);
        } finally {
            return $return;
        }       
    }

    /**
    * Handle the cURL call to the API
    *
    * @throws ApiException
    * @param string $method GET/POST/PUT
    * @param string $uri 
    * @param array $data
    * @param array &$httpCode
    * @return array
    */  
    private function _processRequest($method, $uri, $data = false, &$httpCode = NULL, $allowReconnect = true)
    {

        $ch = curl_init();
        $url = $this->_baseURL . $uri;

        if( 
               ($method == 'POST' || $method == 'PUT') 
            && $data
        ){
            $jsonString = json_encode($data);
            curl_setopt( $ch, CURLOPT_POSTFIELDS, $jsonString );

        }

        if($method == 'POST'){
            curl_setopt($ch, CURLOPT_POST, true);
        } elseif( $method == 'PUT'){
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
        } else {
            if ($data){
                $url = sprintf("%s?%s", $url, http_build_query($data));
            }
        }   

        //set the URL
        curl_setopt($ch, CURLOPT_URL, $url);

        //disable the use of cached connection
        curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);

        //return the respond from the API
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        //return the HEADER respond from the API
        curl_setopt($ch, CURLOPT_HEADER, true);

        //add custom headers
        if(!empty($this->_curlHeader)){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $this->_curlHeader);
        }

        //add the cookie value

        $cookiesFile = 'icwsCookies';
        curl_setopt($ch, CURLOPT_COOKIEJAR, $cookiesFile); // write
        curl_setopt($ch, CURLOPT_COOKIEFILE, $cookiesFile); // read


        //enable SSL
        if( $this->_protocol == 'https' ){
            curl_setopt($ch, CURLOPT_CAINFO, $this->_cainfo);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true);
        }

        //send the request to the API
        $respond = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);  

        //throw cURL exception
        if($respond === false){
            $errorNo = curl_errno($ch);
            $errorMessage = curl_error($ch);

            throw new ApiException($errorMessage, $errorNo);
        }   

        list($header, $body) = explode("\r\n\r\n", $respond, 2);

        if($uri == 'connection'){
            $this->_handleReceivedHeaders($header);
        }

        //if user gets unauthorized error attempt to login as long as the attempt are under 3
        if($httpCode == 401 && $allowReconnect){

            if( $this->_loginAttempts > $this->_maxLoginAttempts){
                throw new ApiException('All Attempts to create a session have been used! Please check your credentials and try again');
            } else {
                $this->_reconnect($method, $uri, $data);
            }

        }

        //convert respond to an array
        return json_decode($body, true);
    }

    /**
    * Reconnect to the Api and generate a new sessionId
    *
    * @return boolean
    */              
    private function _reconnect($method, $uri, $data){

        $this->createSession(true);
        $httpCode = 0;
        $this->_processRequest($method, $uri, $data, $httpCode);

        if($httpCode == 200 || $httpCode == 201){
            return true;
        }

        return false;

    }

    /**
    * Get the cookie HTTP headers and set them as cookie
    *
    * @param array $httpRespond
    * @return void
    */  
    private function _handleReceivedHeaders($httpRespond){

        $header = $this->_http_parse_headers($httpRespond);

        //set the ININ-ICWS-CSRF-Token value
        if( isset($header['ININ-ICWS-CSRF-Token']) ){
            $this->_ININ_ICWS_CSRF_Token = $header['ININ-ICWS-CSRF-Token'];
            setCookie('ININ-ICWS-CSRF-Token', $this->_ININ_ICWS_CSRF_Token);
        }       

    }

    /**
    * Checks if the API return an error
    *
    * @param array $result
    * @return boolean
    */  
    private function _hasAPIError($result){
        if(    isset($result['errorId']) && !empty($result['errorId'])
            && isset($result['message']) && !empty($result['message'])
        ){          
            return true;
        }

        return false;       
    }

    /**
    * Displays the exception details
    *
    * @param ApiException $e
    */  
    private function _displayError(ApiException $e){
        echo 'Error Number: ' . $e->getCode() . "<br>";
        echo $e->getMessage() . "<br><br>";     
    }

     /**
     * convert cURL header into an array
     *
     * @param string $raw_headers
     * @return array
     */ 
    private function _http_parse_headers($raw_headers)
    {
        $headers = array();
        $key = '';

        foreach(explode("\n", $raw_headers) as $i => $h)
        {
            $h = explode(':', $h, 2);

            if (isset($h[1])){
                if (!isset($headers[$h[0]])){
                    $headers[$h[0]] = trim($h[1]);
                } elseif (is_array($headers[$h[0]])){
                    $headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1]))); // [+]
                } else {
                    $headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1]))); // [+]
                }

                $key = $h[0];
            } else {
                if (substr($h[0], 0, 1) == "\t"){
                    $headers[$key] .= "\r\n\t".trim($h[0]);
                } elseif (!$key){
                    $headers[0] = trim($h[0]);trim($h[0]);
                }
            }
        }

        return $headers;
    }

    /**
    * return a valid numeric value
    *
    * @param string $val
    * @return big integer
    */      
    private function _bigint($val){

        $val = filter_var($val, FILTER_SANITIZE_NUMBER_INT);

        if(empty($val)){
            $val = 0;
        }
        return $val;

    }   

    /**
    * Destroy a cookie
    * @return void
    */
    private function _destroy($name){

        setcookie($name, null);
        unset($_COOKIE[$name]);
    }
}

?>

以下是我尝试重新连接到API的地方。 由于某种原因导致循环。

        if($httpCode == 401 && $allowReconnect){

            if( $this->_loginAttempts > $this->_maxLoginAttempts){
                throw new ApiException('All Attempts to create a session have been used! Please check your credentials and try again');
            } else {
                $this->_reconnect($method, $uri, $data);
            }

        }

这是我的代码摘要。 通过$this->createSession(true);创建一个会话$this->createSession(true); 然后在不同的时间调用多个_processRequests()方法。 如果_processRequests()方法返回401,则$this->createSession(true); 被调用,直到返回代码201或200或$this->createSession(true);为止$this->createSession(true); 被称为3次以上,那么我将需要相当。 问题在于,即使$this->createSession(true); 返回代码200或201,它会不断循环,并且不会停止

循环的原因是,当到达错误401时, _processRequests()调用自身为无限。 无法识别第二个呼叫返回201。

_reconnect方法中:

$this->createSession(true);

createSession方法中:

$data = $this->_processRequest('POST', 'connection', $data, $httpCode, false);

_processRequest方法中:

//if user gets unauthorized error attempt to login as long as the attempt are under 3
if($httpCode == 401 && $allowReconnect){

    if( $this->_loginAttempts > $this->_maxLoginAttempts){
        throw new ApiException('All Attempts to create a session have been used! Please check your credentials and try again');
    } else {
        $this->_reconnect($method, $uri, $data);
    }

}

我的猜测是您遇到了未经授权的错误,并且由于您永远不会在代码中的任何地方递增$this->_loginAttempts ,因此它永远不会大于$this->_maxLoginAttempts ,因此代码将再次调用_reconnect ,导致它进入无限循环。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM