curl headers multipart/form 已发送

curl headers multipart/form already sent

尊敬的 Whosebug 用户, 我 运行 遇到了问题。它是这样的: 目前我正在为 pfsense 编写一个管理工具,它需要发送服务器需要验证和处理的多部分表单。它应该在接口上启用基于凭证的访问控制。但是,我得到的错误是我的 headers 已经发送。我没发。

我的代码如下:

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

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

有没有人知道我做错了什么?因为使用以下代码我得到的结果是我的 headers 已经发送:

    $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;

如果能得到所有帮助,我将不胜感激。 提前致谢!

是什么让我的请求生效: 结果发现请求中不需要enctype。但是,需要第 3 次发送更新请求。不要问我为什么。

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

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

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

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

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

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

每次foreach循环都会执行else。可能不会伤害任何东西只是一个错误

   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') 为什么?这是开源的吗?

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

这部分:

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