curl 标头多部分/表单已发送

问题描述 投票:0回答:2

我正在为 pfsense 编写一个管理工具,它需要发送服务器需要验证和处理的多部分表单。它应该在接口上启用基于凭证的访问控制。但是,我收到错误消息,表明我的标头已发送。我没有寄给他们。

我的代码如下:

protected function doCurl($resourceID=null, $post=null)
{
    //volledige url
    $url = Yii::app()->params->pfsense['host'].$resourceID;
    
    $ch = curl_init();
    if($post != null)
    {
        $post_string = "";
        foreach($post as $key=>$value) 
        { 
            if($key != 'enctype')
            {
                $post_string .= $key.'='.$value.'&'; 
            }
            else
            {
                curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                                        'Content-Type: multipart/form-data'
                                        ));
            }
        }
        rtrim($post_string, '&');
        //var_dump($post);
        /**/
        curl_setopt($ch,CURLOPT_POST, count($post));
        curl_setopt($ch,CURLOPT_POSTFIELDS, $post_string);
        //var_dump($post_string);
    }
    else
    {
        curl_setopt($ch, CURLOPT_HEADER, true);
    }
    curl_setopt($ch, CURLOPT_URL, $url);
    //omdat het certificaat niet klopt zetten we de verificatie uit.
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    //we setten de useragent en de timeout. Useragent omdat sommige websites iets anders voorschotelen per browser. 
    //timeout voor als er iets gebeurd wat niet moet
    curl_setopt($ch,CURLOPT_USERAGENT,Yii::app()->params->pfsense['useragent']);
    curl_setopt($ch,CURLOPT_COOKIEJAR, Yii::app()->params->pfsense['cookiepath']);
    curl_setopt($ch,CURLOPT_COOKIEFILE, Yii::app()->params->pfsense['cookiepath']);
    curl_setopt($ch, CURLOPT_AUTOREFERER, true );
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,10);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    $response = curl_exec($ch);
    $result = array( 'header' => '', 
                     'body' => '', 
                     'http_code' => '',
                     'last_url' => '');

    $header_size = curl_getinfo($ch,CURLINFO_HEADER_SIZE);
    $result['header'] = substr($response, 0, $header_size);
    $result['body'] = substr( $response, $header_size );
    $result['http_code'] = curl_getinfo($ch,CURLINFO_HTTP_CODE);
    $result['last_url'] = curl_getinfo($ch,CURLINFO_EFFECTIVE_URL);
    //curl_close($ch);
    return $result;        
}

public function curl($resourceID=null, $post=null)
{
    $result = $this->doCurl($resourceID, $post);
    if(strpos($result['body'], 'Login') == false && $result['http_code'] != 403)
    {
        //echo $result['body'];
        return $result;
    }
    else 
    {
        $loginpost = array(
                        '__csrf_magic' => substr($result['body'], strpos($result['body'],'sid:') , 55),
                        'login' => urlencode('Login'),
                        'usernamefld' => urlencode(Yii::app()->params->pfsense['pfuser']),
                        'passwordfld' => urlencode(Yii::app()->params->pfsense['pfpass'])
                    );
        $result = $this->doCurl('',$loginpost);
        $result = $this->doCurl($resourceID, $post);
        return $result;
    }
}

这是允许将curl请求发送到服务器的代码。如果返回的页面是登录页面,则需要发送登录信息,并重新发送原来的post请求。

下面的代码是插入区域的代码:

public function insertZone($post)
{
    $description = $post['description'];
    $interface = $post['interfaces'];
    $name = $post['name'];
    
    $post=null;
    $post['zone'] = $name;
    $post['descr'] = $description;
    $post['Submit'] = 'Continue';
    $result = $this->curl(Yii::app()->params->pfsense['pfpathtoinsertzone']);
    $post['__csrf_magic'] = substr($result['body'], strpos($result['body'],'sid:') , 55);
    var_dump($post);
    $result = $this->curl(Yii::app()->params->pfsense['pfpathtoinsertzone'], $post);
    var_dump($result['body']);
    //exit;
    if(strpos($result['body'], 'The following input errors were detected') == false)
    {
        $post = null;
        $post['enable'] = 'yes';
        $post['interfaces'] = $interface;
        $post['Submit'] = 'Save';
        $post['name'] = $name;
        
        $result = $this->editZone($post);
        if($result != false)
        {
            $post = null;
            $post['zone'] = $name;
            $post['enable'] = 'yes';
            $post['Submit'] = 'Save';
            
            $result = $this->curl(Yii::app()->params->pfsense['pfpathtovoucherroll'].$name);
            $post['__csrf_magic'] = substr($result['body'], strpos($result['body'],'sid:') , 55);

            $doc = new DOMDocument();
            $doc->loadHTML($result['body']);
            $doc->preserveWhiteSpace = false;
            if($childs = $doc->getElementsByTagName("textarea"))
            {
                foreach($childs as $child)
                {
                    if($child->nodeType == XML_TEXT_NODE)
                    {
                        continue;
                    }
                    if(strpos(trim($child->nodeValue),'BEGIN RSA PRIVATE KEY'))
                    {
                        $post['privatekey'] = trim($child->nodeValue);
                    }
                    elseif(strpos(trim($child->nodeValue),'BEGIN PUBLIC KEY'))
                    {
                        $post['publickey'] = trim($child->nodeValue);
                    }
                }
            }
            $post['charset'] = $doc->getElementById('charset')->attributes->getNamedItem('value')->nodeValue;
            $post['rollbits'] = $doc->getElementById('rollbits')->attributes->getNamedItem('value')->nodeValue;
            $post['ticketbits'] = $doc->getElementById('ticketbits')->attributes->getNamedItem('value')->nodeValue;
            $post['checksumbits'] = $doc->getElementById('checksumbits')->attributes->getNamedItem('value')->nodeValue;
            $post['magic'] = $doc->getElementById('magic')->attributes->getNamedItem('value')->nodeValue;
            $result = $this->curl(Yii::app()->params->pfsense['pfpathtovoucherroll'].$name, $post);
            if($result['http_code'] >= 100 && $result['http_code'] <= 299)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    else 
    {
        return false;
    }
}

public function editZone($post)
{
    $zone = $post['name'];
    $interfaces = $post['interfaces'];
    $post = null;
    //$post['localauth_priv'] = 'yes';
    //$post['radiussrcip_attribute'] = strtolower($interfaces);
    if(is_array($interfaces))
    {
        $post['cinterface[]'] = array_map('strtolower', $interfaces);
    }
    else
    {
        $post['cinterface[]'] = strtolower($interfaces);
    }

    $post['auth_method'] = 'local';
    $post['radiussrcip_attribute'] = 'wan';
    $post['radiusvendor'] = 'default';
    $post['radmac_format'] = 'default';
    $post['enable'] = 'yes';
    $post['Submit'] = 'Save';
    $post["maxprocperip"] = '';
    $post["idletimeout"] = '';
    $post["timeout"] = '';
    $post["freelogins_count"] = '';
    $post["freelogins_resettimeout"] = '';
    $post["preauthurl"] = '';
    $post["redirurl"] = '';
    $post["blockedmacsurl"] = '';
    $post["bwdefaultdn"] = '';
    $post["bwdefaultup"] = '';
    $post["radiusip"] = '';
    $post["radiusport"] = '';
    $post["radiuskey"] = '';
    $post["radiusip2"] = '';
    $post["radiusport2"] = '';
    $post["radiuskey2"] = '';
    $post["radiusip3"] = '';
    $post["radiusport3"] = '';
    $post["radiuskey3"] = '';
    $post["radiusip4"] = '';
    $post["radiusport4"] = '';
    $post["reauthenticateacct"] = '';
    $post["radmac_secret"] = '';
    $post["radiusvendor"] = 'default';
    $post["radiusnasid"] = '';
    $post["radmac_format"] = 'default';
    $post["httpsname"] = '';
    $post['certref'] = '';
    $post['enctype'] = true;

    $post['zone'] = $zone;
    $post['enable'] = 'yes';
    $post['Submit'] = 'Save';
    
    $result = $this->curl(Yii::app()->params->pfsense['pfpathtoupdatezone'].$zone);
    //echo $result['last_url'];
    $post['__csrf_magic'] = substr($result['body'], strpos($result['body'],'sid:') , 55);
    //var_dump($post);
    $result = $this->curl(Yii::app()->params->pfsense['pfpathtoupdatezone'].$zone, $post);
    ini_set('xdebug.var_display_max_depth', -1);
    ini_set('xdebug.var_display_max_children', -1);
    ini_set('xdebug.var_display_max_data', -1);
    var_dump($result['body']);
    exit;
    if($result['http_code'] >= 100 && $result['http_code'] <= 299)
    {
        return true;
    }
    else
    {
        //var_dump($result);
        ///exit;
        return $result;
    }
}

此代码的工作原理是,首先插入具有名称和描述的区域,然后更新它以将界面设置为活动状态并允许显示强制门户页面。但是,如果我发送的页面没有多部分表单(这似乎是问题所在),则身份验证设置不正确。已经设置了,但是不起作用。如果我然后手动更改身份验证设置(它是一个单选按钮,如果我选择另一个单选按钮,然后选择我原来的单选按钮,它会突然起作用)

有人知道我做错了什么吗?因为通过以下代码,我得到的结果是我的标头已发送:

    $result = $this->curl(Yii::app()->params->pfsense['pfpathtoupdatezone'].$zone, $post);
    ini_set('xdebug.var_display_max_depth', -1);
    ini_set('xdebug.var_display_max_children', -1);
    ini_set('xdebug.var_display_max_data', -1);
    var_dump($result['body']);
    exit;
php curl yii
2个回答
0
投票

如果有任何东西,任何东西都会被输出,例如echo, var_dump, 你会得到这个错误。

curl 将标题设置为

application/x-www-form-urlencoded
。如果post数据作为字符串发送。

如果作为数组发送,则使用 Content-Type:multipart/form-data 如果不是这样,请添加以下内容以查看请求标头:

现在我不确定你是如何修复它的,但你可能遇到了问题。

您的数据似乎位于一个数组中,然后由于某种未知原因作为字符串发送。

它应该留在数组中。此代码已关闭。

else 将会在每个 foreach 循环中执行。可能不会伤害任何东西,这只是一个错误

   foreach($post as $key=>$value) 
    { 
        if($key != 'enctype')
        {
            $post_string .= $key.'='.$value.'&'; 
        }
        else
        {
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data'));
        }
    }

应该是:

    if($key == 'enctype'){
       curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data'));  

     }
     else{
        foreach($post as $key=>$value){ 
          $post_string .= $key.'='.$value.'&';
        } 
    }

我认为您将数据作为字符串发送,或者根本没有发送。

这是一个大问题:

if($key != 'enctype')
为什么?这是开源的吗?

仅当发布数据必须编码发送时才会使用上述循环。

这部分:

if($key == 'enctype'){
   curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data'));  
}

应该是:

if($key == 'enctype'){
  $post_string = $post;
}

这样,因为post数据是在数组中,curl会自动使用

Content-Type: multipart/form-data

问题是如果它作为字符串发送,curl将使用

application/x-www-form-urlencoded
,那么你必须在循环之后添加:

$post_string = urlencode($post_string);

像这样:

     else{
        foreach($post as $key=>$value){ 
          $post_string .= $key.'='.$value.'&';
        } 
        $post_string = urlencode($post_string);
      }

0
投票

是什么让我的请求起作用: 结果发现请求中不需要enctype。然而,需要第三次发送更新请求。别问我为什么。

© www.soinside.com 2019 - 2024. All rights reserved.