简体   繁体   English

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

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

I am trying to write a connector using cURL to connect to a REST API. 我正在尝试使用cURL编写连接器以连接到REST API。

The first step the user have to do is creates a session using createSession() . 用户要做的第一步是使用createSession()创建会话。 This will send a POST call to the API with a username and a password. 这将使用用户名和密码将POST调用发送到API。 The API will respond with a sessionId, a cookie value and couple of custom header. API将以sessionId,cookie值和几个自定义标头进行响应。

The session is only valid for 3 minutes after each valid request. 该会话仅在每个有效请求后3分钟内有效。 If I make a request after the session has expired I will get http error code 401 which indicates that the user is unauthorized to make the request because the sessionId is invalid or timed out. 如果我在会话期满后发出请求,我将收到http error code 401 ,该http error code 401表示用户由于sessionId无效或超时而未被授权发出请求。

Instead of asking the user to login again manually, I would like to automatically reconnect by calling the createSession() method when I get error 401. The reason why I need to do this because the sessionId is save in the client cookies and so the client does not know if the session is expired or active. 我不想让用户手动再次登录,而是想在出现错误401时通过调用createSession()方法来自动重新连接。之所以需要这样做是因为sessionId被保存在客户端cookie中,因此客户端不知道会话是否已过期或处于活动状态。 My code will try to call methods using the sessionId that is saved in the cookies weather it is active or expired. 我的代码将尝试使用保存在cookie中的sessionId激活或过期的sessionId来调用方法。

The API will renew the session 3 minutes each time a request is made as long as the session is still active. 只要会话仍处于活动状态,API就会在每次请求时3分钟更新会话。 The only time I will need to reconnect is only when the user have not made a request for 3 minutes. 仅在用户3分钟未提出请求时,我才需要重新连接。

The issue that I am running into is when I try to reconnnect, I go into an infinite loop that I can't figure out how to stop it. 我遇到的问题是,当我尝试重新连接时,我陷入了一个无限循环,无法弄清楚如何阻止它。

Here is my code 这是我的代码

<?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]);
    }
}

?>

This snipit below is where I am trying reconnect to the API. 以下是我尝试重新连接到API的地方。 which is causing the loop for some reason. 由于某种原因导致循环。

        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);
            }

        }

Here is a summary of my code. 这是我的代码摘要。 a session is created via $this->createSession(true); 通过$this->createSession(true);创建一个会话$this->createSession(true); then multiple _processRequests() methods are called at different time. 然后在不同的时间调用多个_processRequests()方法。 If a the _processRequests() method return 401 then $this->createSession(true); 如果_processRequests()方法返回401,则$this->createSession(true); is called until it return code 201 or 200 or the $this->createSession(true); 被调用,直到返回代码201或200或$this->createSession(true);为止$this->createSession(true); is called more than 3 times then I will need to quite. 被称为3次以上,那么我将需要相当。 the problem is even when $this->createSession(true); 问题在于,即使$this->createSession(true); return code 200 or 201 it keeps looping and it does not stop 返回代码200或201,它会不断循环,并且不会停止

The cause of the loop is that _processRequests() calls itself infinite when error 401 is reached. 循环的原因是,当到达错误401时, _processRequests()调用自身为无限。 It does not recognize that the second calls returns 201. 无法识别第二个呼叫返回201。

In _reconnect method: _reconnect方法中:

$this->createSession(true);

In createSession method: createSession方法中:

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

In _processRequest method: _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);
    }

}

My guess is that you got an unauthorized error, and since you're never incrementing $this->_loginAttempts anywhere in the code, it can't never be greater than $this->_maxLoginAttempts , so the code will call again the _reconnect mehtod, causing it to enter in an infinite loop. 我的猜测是您遇到了未经授权的错误,并且由于您永远不会在代码中的任何地方递增$this->_loginAttempts ,因此它永远不会大于$this->_maxLoginAttempts ,因此代码将再次调用_reconnect ,导致它进入无限循环。

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

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