使用workman框架,寫一個簡單的soap服務(wù),訪問wsdl無法正常獲取服務(wù)定義的xml內(nèi)容。經(jīng)調(diào)試后發(fā)現(xiàn)workman收到請求后沒有正確設(shè)置 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';
// 創(chuàng)建一個Worker監(jiān)聽2345端口,使用http協(xié)議通訊
$http_worker = new Worker("http://0.0.0.0:2345");
// 設(shè)置進(jìn)程名稱
$http_worker->name = 'SoapServerWorker';
// 啟動4個進(jìn)程對外提供服務(wù)
$http_worker->count = 1;
class SoapServ {
public function sayHello($name) {
return "Hello, " . $name;
}
}
// 當(dāng)有客戶端連接時
$http_worker->onConnect = function ($connection) {
echo "新連接:{$connection->id}\n";
};
// 接收到瀏覽器發(fā)送的數(shù)據(jù)時回復(fù)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"); //關(guān)閉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();
// 向?yàn)g覽器發(fā)送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 {
// 如果不是預(yù)期的路徑,可以發(fā)送404響應(yīng)或其他邏輯
$connection->send("Not Found");
}
};
// 運(yùn)行worker
Worker::runAll();
調(diào)試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(¶m, service->sdl->source);
ZVAL_STRING(&readfile, "readfile");
if (call_user_function(EG(function_table), NULL, &readfile, &readfile_ret, 1, ¶m ) == FAILURE) {
soap_server_fault("Server", "Couldn't find WSDL", NULL, NULL, NULL);
}
zval_ptr_dtor(¶m);
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)
workman版本信息: "workerman/workerman": "^4.1"
操作系統(tǒng)環(huán)境信息:
[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
webman是一個php cli的框架,不是c擴(kuò)展,沒有辦法直接操作更改內(nèi)核結(jié)構(gòu)體,除非php提供了接口。
使用php cli啟動服務(wù)是會正常賦值的。
啟動服務(wù):
[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
調(diào)試php -S 也是第一步進(jìn)入的是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調(diào)用棧如下:
(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就沒有設(shè)置request_info?
已經(jīng)通過改寫實(shí)現(xiàn)soapserver的功能,手動判斷是否有wsdl的請求,如有則返回wsdl文件內(nèi)容。
代碼如下:
<?php
use Workerman\Worker;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// 創(chuàng)建一個Worker監(jiān)聽2345端口,使用http協(xié)議通訊
$http_worker = new Worker("http://0.0.0.0:2345");
// 將屏幕打印輸出到Worker::$stdoutFile指定的文件中
Worker::$stdoutFile = 'stdout.log';
// 設(shè)置進(jìn)程名稱
$http_worker->name = 'SoapServerWorker';
// 啟動4個進(jìn)程對外提供服務(wù)
$http_worker->count = 1;
class SoapServ {
public function sayHello($name) {
return "Hello, " . $name;
}
}
// 當(dāng)有客戶端連接時
$http_worker->onConnect = function ($connection) {
echo "新連接:{$connection->id}\n";
};
// 接收到瀏覽器發(fā)送的數(shù)據(jù)時回復(fù)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"); //關(guān)閉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();
// 向?yàn)g覽器發(fā)送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 {
// 如果不是預(yù)期的路徑,可以發(fā)送404響應(yīng)或其他邏輯
$connection->send("Not Found");
}
};
// 運(yùn)行worker
Worker::runAll();