代理测试教程
代理测试参考源码(PHP版,含注释)

示例代理:http://ce.monihe.com/proxy.php

<?php
/**
 * 如意册 AJAX跨域请求【代理】功能示范
 * 注意:
 *      !此代理$alow_domains指定允许代理的域名列表,请添加您的域名或关闭此安全设置;
 *      此代理支持files($_FILES)的代理上传提交,但需要PHP5.6及以上版本;;
 *      但不支持header头的代理提交;
 *      使用代理调试、测试目标请求,会增加更多的错误因素(如:header、cookie、referer、编码、跨域 等);
 *      此代理程序仅供参考、示范;
 *      推荐尽量使用直接跨域请求的方案;
 * @author libowen
 * @for 如意册 http://ruyice.com/
 * @date 2017-11
 */

define('APP_DEBUG', true);

if(APP_DEBUG){
    // 注意:跨域请求必须的设置(允许从指定域名发来的请求)
    $origin = !empty($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : (!empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
    if($origin){
        $ruyice_domain = 'ruyice.com';      // 只允许从指定域名发来的请求
        if($_SERVER['HTTP_HOST'] != $ruyice_domain){
            $parseUrl = parse_url($origin);
            if($ruyice_domain === $parseUrl['host']){
                // 必须包括协议scheme
                $allow_origin = $parseUrl['scheme'] . '://' . $parseUrl['host'];

                // 允许指定域名发来的访问请求,即固定限制为客户端网址
                header("Access-Control-Allow-Origin: " . $allow_origin);

                header("Access-Control-Allow-Methods: POST,GET,PUT,OPTIONS,DELETE");

                // 是否允许请求带有验证信息,如cookie
                header("Access-Control-Allow-Credentials: true");

                if('OPTIONS' === strtoupper($_SERVER['REQUEST_METHOD'])){
                    // 支持的请求header字段名
                    header("Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type, SessionID,ASPSESSIONID,ASP.NET_SessionId,JsessionId,PHPSESSID,Authorization,access-token,RuYiCe-DIY-Allow");  // 允许自定义的头部,以逗号隔开,不含下划线,大小写不敏感(RuYiCe-DIY-Allow 可更换为需要ajax发送的header字段名,多个则英文逗号分隔,否则浏览器会屏蔽掉返回的结果)

                    header("HTTP/1.1 204 Accepted");
                    exit;
                }

                /*
                // 开放式开发、测试时,可考虑加个header安全验证
                $access_key = 'access-token';   // 自定义(但要在请求的header中包含,且服务器端Access-Control-Allow-Headers包含)
                $access_password = 'access-token-value';
                $access_key = strtoupper(str_replace('-', '_', $access_key));
                if($access_password !== $_SERVER['HTTP_' . $access_key]){
                    header("Content-type: text/html; charset=utf-8");
                    echo json_encode([
                        'code' => 0,
                        'msg' => 'access-token not right! header头的安全验证错误!',
                        'data' => []
                    ]);
                    exit;
                }
                */

                // 设置为ajax请求方式
                if($_SERVER['HTTP_HOST'] != $ruyice_domain){
                    $_SERVER['HTTP_' . 'X_REQUESTED_WITH'] = 'XMLHttpRequest';
                }
            } else {
                // 非指定域名发来的,则忽略上面的设置
            }
        }
    }
}

// header("content-Type: text/html; charset=UTF-8");
$is_post = false;
if (!empty($_POST)) {
    $is_post = true;
}
$url = (!empty($_GET['source_url']) ? urldecode($_GET['source_url']) : (!empty($_GET['url']) ? urldecode($_GET['url']) : ''));
if (empty($url)) {
    if ($is_post) {
        $url = (!empty($_POST['source_url']) ? $_POST['source_url'] : (!empty($_POST['url']) ? $_POST['url'] : ''));
    }
}
if (empty($url)) {
    $return = [
        'error_code' => 10001,
        'msg' => '未指定url'
    ];
    exit_return($return);
}

// 指定允许的域名,非指定域名则中断并提示
$alow_domains = ['ce.monihe.com', 'ce.dev.monihe.com', 'yourdomain',
    'suggest.taobao.com', 'www.kuaidi100.com', 'baike.baidu.com', 'api.map.baidu.com',
    'm.weather.com.cn', 'php.weather.sina.com.cn', 'qzone-music.qq.com', 'v5.pc.duomi.com',
    'cgi.music.soso.com', 'v.youku.com', 'cache.video.iqiyi.com', 'api.tudou.com',
    'www.tudou.com', 'gc.ditu.aliyun.com', 'www.telize.com', 'ditu.amap.com',
    'int.dpool.sina.com.cn', 'ip.taobao.com', 'tcc.taobao.com', 'virtual.paipai.com',
    'www.baifubao.com', 'cz.115.com', 'www.youdao.com', 'api.showji.com', 'dict.qq.com',
    'r.qzone.qq.com', 'ajax.googleapis.com'];
$url_host = parse_url($url)['host'];
if(!in_array($url_host, $alow_domains)){
    $return = [
        'error_code' => 10002,
        'msg' => '指定被代理的url不在允许范围!请把 '.$url_host.' 添加到代理程序的$alow_domains数组',
        'source_url' => parse_url($url),
        'allow_domains' => isset($alow_domains)?$alow_domains:null
    ];
    exit_return($return);
}

if (!$is_post) {
    if (strpos($url, '?') === false) {
        $url .= '?';
    }
    $get_data = $_GET;
    if($get_data){
        unset($get_data['source_url']);
//    unset($get_data['url']);
        $url .= '&' . http_build_query($get_data);
    }
}

// 提交登录表单请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);

curl_setopt($ch, CURLOPT_TIMEOUT, 5);   //  设置curl执行超时时间最大是多少

// 伪造网页来源地址
//curl_setopt($ch, CURLOPT_REFERER, "http://ruyice.com");

$headers = [
    'X-Requested-With: XMLHttpRequest'      // header头设置此请求为ajax请求
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

// 获取的内容不直接输出,而作为变量
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

if ($is_post) {
    $post_data = $_POST;
    curl_setopt($ch, CURLOPT_POST, true);

    if(empty($_FILES)) {        // 无文件上传提交
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
    } else {                    // 含文件上传提交
//        header("content-Type: text/html; charset=UTF-8");
//        $return = [
//            'error_code' => 10003,
//            'msg' => '此代理不支持文件上传提交!',
//            'source_url' => parse_url($url),
//            '$_POST' => $_POST,
//            '$_FILES' => $_FILES
//        ];
//        exit_return($return);

        // 限制文件数量和大小 file_limit=5,max_size=512000
        $is_error = 0;
        $file_limit = 5;
        $max_size = 512000;
        $file_count = 0;
        $files_data = [];
        foreach($_FILES as $key => $value){
            if(is_array($value['tmp_name'])){
                $temp_count = count($value['tmp_name']);
                for($i=0; $i<$temp_count; $i++){
                    $file_count++;
                    if($value['error'][$i] > 0){
                        $is_error = 1;
                        break;
                    }
                    if($value['size'][$i] > $max_size){
                        $is_error = 2;
                        break;
                    }
                    // PHP版本5.6及以上,curl文件上传必须使用CurlFile对象
                    $files_data[$key.'['.$i.']'] = new \CURLFile(realpath($value['tmp_name'][$i]), $value['type'][$i], $value['name'][$i]);
                }
            } else {
                $file_count++;
                if($value['error'] > 0){
                    $is_error = 1;
                    break;
                }
                if($value['size'] > $max_size){
                    $is_error = 2;
                    break;
                }
                // PHP版本5.6及以上,curl文件上传必须使用CurlFile对象
                $files_data[$key] = new \CURLFile(realpath($value['tmp_name']), $value['type'], $value['name']);
            }
            if($file_count > $file_limit){
                $return = [
                    'error_code' => 10004,
                    'msg' => '已超过代理程序允许上传文件数量:' . $file_limit,
                    '$_FILES' => $_FILES
                ];
                exit_return($return);
            }
            if($is_error === 1){
                $return = [
                    'error_code' => 10005,
                    'msg' => '提交到代理程序的文件有错误!',
                    '$_FILES' => $_FILES
                ];
                exit_return($return);
            } elseif($is_error === 2){
                $return = [
                    'error_code' => 10006,
                    'msg' => '提交到代理程序的文件太大!',
                    '$_FILES' => $_FILES
                ];
                exit_return($return);
            } else {}
        }
//        exit_return($files_data);

        $post_data = array_merge($post_data, $files_data);
        curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    }
}

// 注意:不同服务器端开发语言cookie对应session的id名是不同的:SessionID,ASPSESSIONID,ASP.NET_SessionId,JsessionId,PHPSESSID
$lang_config = [
    'php' => 'PHPSESSID',
    'asp' => 'ASPSESSIONID',
    'aspx' => 'ASP.NET_SessionId',
    'asp.net' => 'ASP.NET_SessionId',
    'jsp' => 'JsessionId',
    'default' => 'SessionID',
];
$url_lang = 'php';      // 被代理地址的服务器端开发语言对应的配置
// 设置cookie
$cookie = '';
////foreach($_COOKIE as $key => $value){【注意:高危使用,请谨慎使用$_COOKIE】
////    if($key == 'PHPSESSID'){
////        $key = $lang_config[$url_lang];
////    }
////    $cookie.= $key . '=' . $value . ';';
////}
$session_id = substr(md5(date('Y-m-d')), 0, 26);    // 请自定义
foreach ($lang_config as $value){
    $cookie.= $value . '=' . $session_id . ';';
}
if($cookie){
    curl_setopt($ch, CURLOPT_COOKIE, $cookie);
}

// 参数为0表示不带头文件,为1表示带头文件
curl_setopt($ch, CURLOPT_HEADER, false);

$output = curl_exec($ch);
curl_close($ch);

echo $output;
exit;


function exit_return($return_data)
{
    header("content-Type: text/html; charset=UTF-8");
    echo json_encode($return_data);
    exit;
}



如意册首页↗ 接口测试台↗ 控制台↗ 示范项目↗