workman没有正确设置sapi_globals_struct的request_info

venchi

问题描述

使用workman框架,写一个简单的soap服务,访问wsdl无法正常获取服务定义的xml内容。经调试后发现workman收到请求后没有正确设置 sapi_globals_struct.request_info

程序代码或配置

workman代码

<?php
use Workerman\Worker;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

// 创建一个Worker监听2345端口,使用http协议通讯
$http_worker = new Worker("http://0.0.0.0:2345");

// 设置进程名称
$http_worker->name = 'SoapServerWorker';

// 启动4个进程对外提供服务
$http_worker->count = 1;

class SoapServ {
    public function sayHello($name) {
        return "Hello, " . $name;
    }
}

// 当有客户端连接时
$http_worker->onConnect = function ($connection) {
    echo "新连接:{$connection->id}\n";
};

// 接收到浏览器发送的数据时回复hello world给浏览器
$http_worker->onMessage = function(TcpConnection $connection, Request $request)
{
    if ($request->path() == '/serv') {
        try {

            ini_set('display_errors', 'On');
            ini_set('soap.wsdl_cache_enabled', "0"); //关闭wsdl缓存
            ob_start();
            $server = new SoapServer('/var/www/interfacems/workman/webman.wsdl', array('soap_version' => SOAP_1_2));
            $server->setClass('SoapServ');
            $server->handle();
            // readfile('webman.wsdl');
            $ret = ob_get_clean();
            $response = new Response(200, [
                'Content-Type' => 'text/xml; charset=utf-8',
            ], $ret);

            $connection->send($response);
            return;
        } catch (Exception $e) {
            // 错误处理逻辑
            $connection->send("Error: " . $e->getMessage());
        }
    } elseif ($request->path() == '/client') {
        try {
            $client = new SoapClient("http://localhost:8080/serv?wsdl");
            $fs = $client->__getFunctions();
            // 向浏览器发送hello world
            $response = new Response(200, [
                'Content-Type' => 'text/json; charset=utf-8',
            ], json_encode($fs, JSON_UNESCAPED_UNICODE));
            $connection->send($response);
        } catch (Exception $e) {
            // 错误处理逻辑
            $connection->send("Error: " . $e->getMessage());
        }
    } else {
        // 如果不是预期的路径,可以发送404响应或其他逻辑
        $connection->send("Not Found");
    }
};

// 运行worker
Worker::runAll();

调试soap代码 soap.c:1158


    printf("request_method = %s query_string=%s \n", SG(request_info).request_method, SG(request_info).query_string); 
    // 这里修改了源码,打印了request_info属性值,输出为null

    if (SG(request_info).request_method &&
        strcmp(SG(request_info).request_method, "GET") == 0 &&
        SG(request_info).query_string &&
        stricmp(SG(request_info).query_string, "wsdl") == 0) {

        if (service->sdl) {
/*
            char *hdr = emalloc(sizeof("Location: ")+strlen(service->sdl->source));
            strcpy(hdr,"Location: ");
            strcat(hdr,service->sdl->source);
            sapi_add_header(hdr, sizeof("Location: ")+strlen(service->sdl->source)-1, 1);
            efree(hdr);
*/
            zval readfile, readfile_ret, param;

            sapi_add_header("Content-Type: text/xml; charset=utf-8", sizeof("Content-Type: text/xml; charset=utf-8")-1, 1);
            ZVAL_STRING(&param, service->sdl->source);
            ZVAL_STRING(&readfile, "readfile");
            if (call_user_function(EG(function_table), NULL, &readfile, &readfile_ret, 1, &param ) == FAILURE) {
                soap_server_fault("Server", "Couldn't find WSDL", NULL, NULL, NULL);
            }

            zval_ptr_dtor(&param);
            zval_ptr_dtor_str(&readfile);
            zval_ptr_dtor(&readfile_ret);

            SOAP_SERVER_END_CODE();
            return;
        } else {
            soap_server_fault("Server", "WSDL generation is not supported yet", NULL, NULL, NULL);
/*
            sapi_add_header("Content-Type: text/xml; charset=utf-8", sizeof("Content-Type: text/xml; charset=utf-8"), 1);
            PUTS("<?xml version=\"1.0\" ?>\n<definitions\n");
            PUTS("    xmlns=\"http://schemas.xmlsoap.org/wsdl/\"\n");
            PUTS("    targetNamespace=\"");
            PUTS(service->uri);
            PUTS("\">\n");
            PUTS("</definitions>");
*/
            SOAP_SERVER_END_CODE();
            return;
        }

输出:

[root@localhost workman]# php test.php start
Workerman[test.php] start in DEBUG mode
------------------------------------------- WORKERMAN --------------------------------------------
Workerman version:4.1.15          PHP version:8.3.6           Event-Loop:\Workerman\Events\Select
-------------------------------------------- WORKERS ---------------------------------------------
proto   user            worker              listen                 processes    status           
tcp     root            SoapServerWorker    http://0.0.0.0:2345    1             [OK]            
--------------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.

request_method = (null) query_string=(null)

操作系统环境及workerman/webman等具体版本

workman版本信息: "workerman/workerman": "^4.1"

操作系统环境信息:

[root@localhost php-8.3.6]# cat /etc/os-release 
NAME="openEuler"
VERSION="22.03 (LTS-SP3)"
ID="openEuler"
VERSION_ID="22.03"
PRETTY_NAME="openEuler 22.03 (LTS-SP3)"
ANSI_COLOR="0;31"

[root@localhost php-8.3.6]# php -v
PHP 8.3.6 (cli) (built: Jul  4 2024 11:02:42) (ZTS DEBUG)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
[root@localhost php-8.3.6]# php --ri soap

soap

Soap Client => enabled
Soap Server => enabled

Directive => Local Value => Master Value
soap.wsdl_cache_enabled => On => On
soap.wsdl_cache_dir => /tmp => /tmp
soap.wsdl_cache_ttl => 86400 => 86400
soap.wsdl_cache => 1 => 1
soap.wsdl_cache_limit => 5 => 5
285 2 0
2个回答

walkor

webman是一个php cli的框架,不是c扩展,没有办法直接操作更改内核结构体,除非php提供了接口。

  • venchi 2024-07-04

    使用php cli启动服务是会正常赋值的。
    启动服务:

    [root@localhost interfacems]# php -S 0.0.0.0:2345
    [Thu Jul  4 16:55:03 2024] PHP 8.3.6 Development Server (http://0.0.0.0:2345) started
    [Thu Jul  4 16:55:21 2024] 127.0.0.1:38046 Accepted
    request_method = GET query_string=wsdl 
    [Thu Jul  4 16:55:21 2024] 127.0.0.1:38046 [200]: GET /soap.php?wsdl
    [Thu Jul  4 16:55:21 2024] 127.0.0.1:38046 Closing
    [Thu Jul  4 16:56:40 2024] 127.0.0.1:36138 Accepted
    request_method = GET query_string=wsdl 

    测试代码:

    <?php
    class SoapServ {
        public function sayHello($name) {
            return "Hello, " . $name;
        }
    }
    
    $server = new SoapServer('/var/www/interfacems/workman/webman.wsdl', array('soap_version' => SOAP_1_2));
    $server->setClass('SoapServ');
    $server->handle();

    测试命令:curl http://localhost:2345/soap.php?wsdl

  • walkor 2024-07-04

    php -S 是启动php内置的server,和php cli不是一回事

  • venchi 2024-07-05

    调试php -S 也是第一步进入的是sapi/cli下面的cli_server
    php -S栈信息如下:

    (gdb) bt
    #0  0x00007f9e76627289 in select () from /usr/lib64/libc.so.6
    #1  0x0000000000d2b154 in php_cli_server_poller_poll (poller=0x1c23bc8 <server+8>, tv=0x7ffe9a7a5c20)
        at /root/php/php-8.3.6/sapi/cli/php_cli_server.c:880
    #2  0x0000000000d2f71c in php_cli_server_do_event_loop (server=0x1c23bc0 <server>) at /root/php/php-8.3.6/sapi/cli/php_cli_server.c:2756
    #3  0x0000000000d2fabd in do_cli_server (argc=3, argv=0x2f7fa60) at /root/php/php-8.3.6/sapi/cli/php_cli_server.c:2890
    #4  0x0000000000d25ca9 in main (argc=3, argv=0x2f7fa60) at /root/php/php-8.3.6/sapi/cli/php_cli.c:1343

    workman调用栈如下:

    (gdb) zbacktrace 
    [0x7f87e6c17820] pcntl_wait(reference, 2) [internal function]
    [0x7f87e6c172f0] Workerman\Worker->monitorWorkersForLinux() /var/www/interfacems/workman/vendor/workerman/workerman/Worker.php:1743 
    [0x7f87e6c17250] Workerman\Worker->monitorWorkers() /var/www/interfacems/workman/vendor/workerman/workerman/Worker.php:1724 
    [0x7f87e6c17130] Workerman\Worker->runAll() /var/www/interfacems/workman/vendor/workerman/workerman/Worker.php:562 
    [0x7f87e6c17020] (main) /var/www/interfacems/workman/test.php:72 
    (gdb) bt
    #0  0x00007f87e6fe016a in wait4 () from /usr/lib64/libc.so.6
    #1  0x000000000087f51b in zif_pcntl_wait (execute_data=0x7f87e6c17820, return_value=0x7f87e6c173f0) at /root/php/php-8.3.6/ext/pcntl/pcntl.c:325
    #2  0x0000000000be891d in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER () at /root/php/php-8.3.6/Zend/zend_vm_execute.h:1337
    #3  0x0000000000c667ca in execute_ex (ex=0x7f87e6c17020) at /root/php/php-8.3.6/Zend/zend_vm_execute.h:57216
    #4  0x0000000000c6b085 in zend_execute (op_array=0x7f87e6c7d140, return_value=0x0) at /root/php/php-8.3.6/Zend/zend_vm_execute.h:61604
    #5  0x0000000000ba72b4 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /root/php/php-8.3.6/Zend/zend.c:1891
    #6  0x0000000000aec860 in php_execute_script (primary_file=0x7ffd8516b780) at /root/php/php-8.3.6/main/main.c:2507
    #7  0x0000000000d24f57 in do_cli (argc=3, argv=0x376df50) at /root/php/php-8.3.6/sapi/cli/php_cli.c:966
    #8  0x0000000000d25c8d in main (argc=3, argv=0x376df50) at /root/php/php-8.3.6/sapi/cli/php_cli.c:1340

    看了源码是一个是 do_cli 一个是do_cli_server,难道是php cli就没有设置request_info?

venchi

已经通过改写实现soapserver的功能,手动判断是否有wsdl的请求,如有则返回wsdl文件内容。
代码如下:

<?php
use Workerman\Worker;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

// 创建一个Worker监听2345端口,使用http协议通讯
$http_worker = new Worker("http://0.0.0.0:2345");

// 将屏幕打印输出到Worker::$stdoutFile指定的文件中
Worker::$stdoutFile = 'stdout.log';

// 设置进程名称
$http_worker->name = 'SoapServerWorker';

// 启动4个进程对外提供服务
$http_worker->count = 1;

class SoapServ {
    public function sayHello($name) {
        return "Hello, " . $name;
    }
}

// 当有客户端连接时
$http_worker->onConnect = function ($connection) {
    echo "新连接:{$connection->id}\n";
};

// 接收到浏览器发送的数据时回复hello world给浏览器
$http_worker->onMessage = function(TcpConnection $connection, Request $request)
{
    if ($request->path() == '/serv') {
        try {

            // ini_set('display_errors', 'On');
            // ini_set('soap.wsdl_cache_enabled', "0"); //关闭wsdl缓存

            $wsdl = file_get_contents('/var/www/interfacems/workman/webman.wsdl');
            if ($request->method() == 'GET' && $request->get('wsdl') !== null) {
                $response = new Response(200, [
                    'Content-Type' => 'application/xml; charset=utf-8',
                ], $wsdl);
                $connection->send($response);
                return;
            }

            $server = new SoapServer('/var/www/interfacems/workman/webman.wsdl', array('soap_version' => SOAP_1_2));
            $server->setClass('SoapServ');
            ob_start();
            $server->handle();
            // readfile('webman.wsdl');
            $ret = ob_get_clean();
            $response = new Response(200, [
                'Content-Type' => 'text/xml; charset=utf-8',
            ], $ret);

            $connection->send($response);
            return;
        } catch (Exception $e) {
            // 错误处理逻辑
            $connection->send("Error: " . $e->getMessage());
        }
    } elseif ($request->path() == '/client') {
        try {
            $client = new SoapClient("http://localhost:8080/serv?wsdl");
            $fs = $client->__getFunctions();
            // 向浏览器发送hello world
            $response = new Response(200, [
                'Content-Type' => 'text/json; charset=utf-8',
            ], json_encode($fs, JSON_UNESCAPED_UNICODE));
            $connection->send($response);
        } catch (Exception $e) {
            // 错误处理逻辑
            $connection->send("Error: " . $e->getMessage());
        }
    } else {
        // 如果不是预期的路径,可以发送404响应或其他逻辑
        $connection->send("Not Found");
    }
};

// 运行worker
Worker::runAll();
  • 暂无评论
×
🔝