walkor / workerman

An asynchronous event driven PHP socket framework. Supports HTTP, Websocket, SSL and other custom protocols.
http://www.workerman.net
MIT License
11.03k stars 2.25k forks source link

workman没有正确设置sapi_globals_struct的request_info #1040

Open alreadyshow opened 3 days ago

alreadyshow commented 3 days ago

问题描述

使用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
walkor commented 3 days ago

那么问题来了,PHP cli里如何正确设置 sapi_globals_struct的request_info ?

alreadyshow commented 4 hours ago

已经通过改写实现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();