Metrics
0
Watch 281 Star 1.1K Fork 248

GVPswoole / swoole-srcCApache-2.0

Sign up for free
Explore and code with more than 2 million developers,Free private repositories !:)
Sign up
PHP的异步、并行、高性能网络通信引擎 spread retract

http://www.swoole.com/

  • C++ 38.0%
  • PHP 35.6%
  • C 25.3%
  • M4 0.6%
  • Shell 0.2%
  • Other 0.3%
Clone or download
swoole_http2_client_coro.cc 51.88 KB
Copy Edit Web IDE Raw Blame History
twosee authored 2019-06-14 14:29 . Add sw_ namespace
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522
/*
+----------------------------------------------------------------------+
| Swoole |
+----------------------------------------------------------------------+
| This source file is subject to version 2.0 of the Apache license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.apache.org/licenses/LICENSE-2.0.html |
| If you did not receive a copy of the Apache2.0 license and are unable|
| to obtain it through the world-wide-web, please send a note to |
| license@swoole.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
+----------------------------------------------------------------------+
*/
#include "php_swoole_cxx.h"
#include "swoole_http.h"
#ifdef SW_USE_HTTP2
#include "http.h"
#include "http2.h"
#define HTTP2_CLIENT_HOST_HEADER_INDEX 3
#include <vector>
using namespace swoole;
using swoole::coroutine::Socket;
static zend_class_entry *swoole_http2_client_coro_ce;
static zend_object_handlers swoole_http2_client_coro_handlers;
static zend_class_entry *swoole_http2_client_coro_exception_ce;
static zend_object_handlers swoole_http2_client_coro_exception_handlers;
static zend_class_entry *swoole_http2_request_ce;
static zend_object_handlers swoole_http2_request_handlers;
static zend_class_entry *swoole_http2_response_ce;
static zend_object_handlers swoole_http2_response_handlers;
struct http2_client_stream
{
uint32_t stream_id;
uint8_t gzip;
uint8_t type;
swString *buffer;
#ifdef SW_HAVE_ZLIB
z_stream gzip_stream;
swString *gzip_buffer;
#endif
zval *zresponse;
zval _zresponse;
// flow control
uint32_t remote_window_size;
uint32_t local_window_size;
};
class http2_client
{
public:
std::string host;
int port;
bool ssl;
double timeout = Socket::default_read_timeout;
Socket *client = nullptr;
nghttp2_hd_inflater *inflater = nullptr;
nghttp2_hd_deflater *deflater = nullptr;
uint32_t stream_id = 0; // the next send stream id
uint32_t last_stream_id = 0; // the last received stream id
swHttp2_settings local_settings = {0};
swHttp2_settings remote_settings = {0};
swHashMap *streams = nullptr;
/* safety zval */
zval _zobject;
zval *zobject;
http2_client(const char *_host, size_t _host_len, int _port, bool _ssl, zval *__zobject)
{
host = std::string(_host, _host_len);
port = _port;
ssl = _ssl;
_zobject = *__zobject;
zobject = &_zobject;
}
inline http2_client_stream* get_stream(uint32_t stream_id)
{
return (http2_client_stream*) swHashMap_find_int(streams, stream_id);
}
inline void update_error_properties(int code, const char *msg)
{
zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errCode"), code);
zend_update_property_string(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errMsg"), msg);
}
inline void io_error()
{
update_error_properties(client->errCode, client->errMsg);
}
inline void nghttp2_error(int code, const char *msg)
{
update_error_properties(code, cpp_string::format("%s with error: %s", msg, nghttp2_strerror(code)).c_str());
}
inline bool is_available()
{
if (unlikely(!client))
{
SwooleG.error = SW_ERROR_CLIENT_NO_CONNECTION;
zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errCode"), ECONNRESET);
zend_update_property_string(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errMsg"), "client is not connected to server");
return false;
}
return true;
}
inline void apply_setting(zval *zset)
{
if (client && ZVAL_IS_ARRAY(zset))
{
php_swoole_client_set(client, zset);
}
}
inline bool recv_packet(double timeout)
{
if (unlikely(client->recv_packet(timeout) <= 0))
{
io_error();
return false;
}
return true;
}
bool connect();
http2_client_stream* create_stream(uint32_t stream_id, bool pipeline);
bool send_window_update(int stream_id, uint32_t size);
bool send_ping_frame();
bool send_data(uint32_t stream_id, zval *data, bool end);
uint32_t send_request(zval *req);
bool send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len);
enum swReturnType parse_frame(zval *return_value);
bool close();
~http2_client()
{
close();
}
private:
bool send_setting();
int parse_header(http2_client_stream *stream , int flags, char *in, size_t inlen);
inline bool send(const char *buf, size_t len)
{
if (unlikely(client->send_all(buf, len) != (ssize_t )len))
{
io_error();
return false;
}
return true;
}
};
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_construct, 0, 0, 1)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, port)
ZEND_ARG_INFO(0, ssl)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_set, 0, 0, 1)
ZEND_ARG_ARRAY_INFO(0, settings, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_stats, 0, 0, 0)
ZEND_ARG_INFO(0, key)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_isStreamExist, 0, 0, 1)
ZEND_ARG_INFO(0, stream_id)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_send, 0, 0, 1)
ZEND_ARG_INFO(0, request)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_write, 0, 0, 2)
ZEND_ARG_INFO(0, stream_id)
ZEND_ARG_INFO(0, data)
ZEND_ARG_INFO(0, end_stream)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_recv, 0, 0, 0)
ZEND_ARG_INFO(0, timeout)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http2_client_coro_goaway, 0, 0, 0)
ZEND_ARG_INFO(0, error_code)
ZEND_ARG_INFO(0, debug_data)
ZEND_END_ARG_INFO()
static PHP_METHOD(swoole_http2_client_coro, __construct);
static PHP_METHOD(swoole_http2_client_coro, __destruct);
static PHP_METHOD(swoole_http2_client_coro, set);
static PHP_METHOD(swoole_http2_client_coro, connect);
static PHP_METHOD(swoole_http2_client_coro, stats);
static PHP_METHOD(swoole_http2_client_coro, isStreamExist);
static PHP_METHOD(swoole_http2_client_coro, send);
static PHP_METHOD(swoole_http2_client_coro, write);
static PHP_METHOD(swoole_http2_client_coro, recv);
static PHP_METHOD(swoole_http2_client_coro, ping);
static PHP_METHOD(swoole_http2_client_coro, goaway);
static PHP_METHOD(swoole_http2_client_coro, close);
static void http2_client_stream_free(void *ptr);
static const zend_function_entry swoole_http2_client_methods[] =
{
PHP_ME(swoole_http2_client_coro, __construct, arginfo_swoole_http2_client_coro_construct, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, set, arginfo_swoole_http2_client_coro_set, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, connect, arginfo_swoole_void, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, stats, arginfo_swoole_http2_client_coro_stats, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, isStreamExist, arginfo_swoole_http2_client_coro_isStreamExist, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, send, arginfo_swoole_http2_client_coro_send, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, write, arginfo_swoole_http2_client_coro_write, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, recv, arginfo_swoole_http2_client_coro_recv, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, goaway, arginfo_swoole_http2_client_coro_goaway, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, ping, arginfo_swoole_void, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http2_client_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC)
PHP_FE_END
};
void swoole_http2_client_coro_init(int module_number)
{
SW_INIT_CLASS_ENTRY(swoole_http2_client_coro, "Swoole\\Coroutine\\Http2\\Client", NULL, "Co\\Http2\\Client", swoole_http2_client_methods);
SW_SET_CLASS_SERIALIZABLE(swoole_http2_client_coro, zend_class_serialize_deny, zend_class_unserialize_deny);
SW_SET_CLASS_CLONEABLE(swoole_http2_client_coro, sw_zend_class_clone_deny);
SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_client_coro, sw_zend_class_unset_property_deny);
SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_client_coro);
SW_INIT_CLASS_ENTRY_EX(swoole_http2_client_coro_exception, "Swoole\\Coroutine\\Http2\\Client\\Exception", NULL, "Co\\Http2\\Client\\Exception", NULL, swoole_exception);
SW_INIT_CLASS_ENTRY(swoole_http2_request, "Swoole\\Http2\\Request", "swoole_http2_request", NULL, NULL);
SW_SET_CLASS_SERIALIZABLE(swoole_http2_request, zend_class_serialize_deny, zend_class_unserialize_deny);
SW_SET_CLASS_CLONEABLE(swoole_http2_request, sw_zend_class_clone_deny);
SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_request, sw_zend_class_unset_property_deny);
SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_request);
SW_INIT_CLASS_ENTRY(swoole_http2_response, "Swoole\\Http2\\Response", "swoole_http2_response", NULL, NULL);
SW_SET_CLASS_SERIALIZABLE(swoole_http2_response, zend_class_serialize_deny, zend_class_unserialize_deny);
SW_SET_CLASS_CLONEABLE(swoole_http2_response, sw_zend_class_clone_deny);
SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_response, sw_zend_class_unset_property_deny);
SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_response);
zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("errMsg"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC);
zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC);
zend_declare_property_bool(swoole_http2_client_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL("host"), ZEND_ACC_PUBLIC);
zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_bool(swoole_http2_client_coro_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("path"), "/", ZEND_ACC_PUBLIC);
zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("method"), "GET", ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http2_request_ce, ZEND_STRL("headers"), ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http2_request_ce, ZEND_STRL("cookies"), ZEND_ACC_PUBLIC);
zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL("data"), "", ZEND_ACC_PUBLIC);
zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL("pipeline"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("streamId"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("statusCode"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_bool(swoole_http2_response_ce, ZEND_STRL("pipeline"), 0, ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("headers"), ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("set_cookie_headers"), ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("cookies"), ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_DATA", SW_HTTP2_TYPE_DATA);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_HEADERS", SW_HTTP2_TYPE_HEADERS);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PRIORITY", SW_HTTP2_TYPE_PRIORITY);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_RST_STREAM", SW_HTTP2_TYPE_RST_STREAM);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_SETTINGS", SW_HTTP2_TYPE_SETTINGS);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PUSH_PROMISE", SW_HTTP2_TYPE_PUSH_PROMISE);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PING", SW_HTTP2_TYPE_PING);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_GOAWAY", SW_HTTP2_TYPE_GOAWAY);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_WINDOW_UPDATE", SW_HTTP2_TYPE_WINDOW_UPDATE);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_CONTINUATION", SW_HTTP2_TYPE_CONTINUATION);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_NO_ERROR", SW_HTTP2_ERROR_NO_ERROR);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_PROTOCOL_ERROR", SW_HTTP2_ERROR_PROTOCOL_ERROR);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_INTERNAL_ERROR", SW_HTTP2_ERROR_INTERNAL_ERROR);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_FLOW_CONTROL_ERROR", SW_HTTP2_ERROR_FLOW_CONTROL_ERROR);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_SETTINGS_TIMEOUT", SW_HTTP2_ERROR_SETTINGS_TIMEOUT);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_STREAM_CLOSED", SW_HTTP2_ERROR_STREAM_CLOSED);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_FRAME_SIZE_ERROR", SW_HTTP2_ERROR_FRAME_SIZE_ERROR);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_REFUSED_STREAM", SW_HTTP2_ERROR_REFUSED_STREAM);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_CANCEL", SW_HTTP2_ERROR_CANCEL);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_COMPRESSION_ERROR", SW_HTTP2_ERROR_COMPRESSION_ERROR);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_CONNECT_ERROR", SW_HTTP2_ERROR_CONNECT_ERROR);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_ENHANCE_YOUR_CALM", SW_HTTP2_ERROR_ENHANCE_YOUR_CALM);
SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_INADEQUATE_SECURITY", SW_HTTP2_ERROR_INADEQUATE_SECURITY);
}
bool http2_client::connect()
{
if (unlikely(client != nullptr))
{
return false;
}
client = new Socket(SW_SOCK_TCP);
client->http2 = 1;
client->open_length_check = 1;
client->protocol.get_package_length = swHttp2_get_frame_length;
client->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE;
client->protocol.package_max_length = SW_BUFFER_INPUT_SIZE;
apply_setting(sw_zend_read_property(swoole_http2_client_coro_ce, zobject, ZEND_STRL("setting"), 0));
#ifdef SW_USE_OPENSSL
client->open_ssl = ssl;
#endif
if (!client->connect(host, port))
{
io_error();
close();
return false;
}
stream_id = 1;
streams = swHashMap_new(8, http2_client_stream_free);
// [init]: we must set default value, server is not always send all the settings
swHttp2_init_settings(&local_settings);
swHttp2_init_settings(&remote_settings);
int ret = nghttp2_hd_inflate_new(&inflater);
if (ret != 0)
{
nghttp2_error(ret, "nghttp2_hd_inflate_new() failed");
close();
return false;
}
ret = nghttp2_hd_deflate_new(&deflater, local_settings.header_table_size);
if (ret != 0)
{
nghttp2_error(ret, "nghttp2_hd_deflate_new() failed");
close();
return false;
}
if (!send(ZEND_STRL(SW_HTTP2_PRI_STRING)))
{
close();
return false;
}
if (!send_setting())
{
close();
return false;
}
zend_update_property_bool(swoole_http2_client_coro_ce, zobject, ZEND_STRL("connected"), 1);
return true;
}
bool http2_client::close()
{
Socket *_client = client;
if (!_client)
{
return false;
}
zend_update_property_bool(swoole_http2_client_coro_ce, zobject, ZEND_STRL("connected"), 0);
if (!_client->has_bound())
{
if (streams)
{
swHashMap_free(streams);
streams = NULL;
}
if (inflater)
{
nghttp2_hd_inflate_del(inflater);
inflater = NULL;
}
if (deflater)
{
nghttp2_hd_deflate_del(deflater);
deflater = NULL;
}
client = nullptr;
}
if (_client->close())
{
delete _client;
}
return true;
}
enum swReturnType http2_client::parse_frame(zval *return_value)
{
char *buf = client->get_read_buffer()->str;
uint8_t type = buf[3];
uint8_t flags = buf[4];
uint32_t stream_id = ntohl((*(int *) (buf + 5))) & 0x7fffffff;
ssize_t length = swHttp2_get_length(buf);
buf += SW_HTTP2_FRAME_HEADER_SIZE;
char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE];
if (stream_id > last_stream_id)
{
last_stream_id = stream_id;
}
uint16_t id = 0;
uint32_t value = 0;
switch (type)
{
case SW_HTTP2_TYPE_SETTINGS:
{
if (flags & SW_HTTP2_FLAG_ACK)
{
swHttp2FrameTraceLog(recv, "ACK");
return SW_CONTINUE;
}
while (length > 0)
{
id = ntohs(*(uint16_t *) (buf));
value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t)));
swHttp2FrameTraceLog(recv, "id=%d, value=%d", id, value);
switch (id)
{
case SW_HTTP2_SETTING_HEADER_TABLE_SIZE:
if (value != remote_settings.header_table_size)
{
remote_settings.header_table_size = value;
int ret = nghttp2_hd_deflate_change_table_size(deflater, value);
if (ret != 0)
{
nghttp2_error(ret, "nghttp2_hd_deflate_change_table_size() failed");
return SW_ERROR;
}
}
swTraceLog(SW_TRACE_HTTP2, "setting: header_compression_table_max=%u", value);
break;
case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
remote_settings.max_concurrent_streams = value;
swTraceLog(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value);
break;
case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE:
remote_settings.window_size = value;
swTraceLog(SW_TRACE_HTTP2, "setting: init_send_window=%u", value);
break;
case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE:
remote_settings.max_frame_size = value;
swTraceLog(SW_TRACE_HTTP2, "setting: max_frame_size=%u", value);
break;
case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
if (value != remote_settings.max_header_list_size)
{
remote_settings.max_header_list_size = value;
int ret = nghttp2_hd_inflate_change_table_size(inflater, value);
if (ret != 0)
{
nghttp2_error(ret, "nghttp2_hd_inflate_change_table_size() failed");
return SW_ERROR;
}
}
swTraceLog(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value);
break;
default:
// disable warning and ignore it because some websites are not following http2 protocol totally
// swWarn("unknown option[%d]: %d", id, value);
break;
}
buf += sizeof(id) + sizeof(value);
length -= sizeof(id) + sizeof(value);
}
swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_SETTINGS, 0, SW_HTTP2_FLAG_ACK, stream_id);
if (!send(frame, SW_HTTP2_FRAME_HEADER_SIZE))
{
return SW_ERROR;
}
return SW_CONTINUE;
}
case SW_HTTP2_TYPE_WINDOW_UPDATE:
{
value = ntohl(*(uint32_t *) buf);
swHttp2FrameTraceLog(recv, "window_size_increment=%d", value);
if (stream_id == 0)
{
remote_settings.window_size += value;
}
else
{
http2_client_stream *stream = get_stream(stream_id);
if (stream)
{
stream->remote_window_size += value;
}
}
return SW_CONTINUE;
}
case SW_HTTP2_TYPE_PING:
{
swHttp2FrameTraceLog(recv, "ping");
if (!(flags & SW_HTTP2_FLAG_ACK))
{
swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_ACK, stream_id);
memcpy(frame + SW_HTTP2_FRAME_HEADER_SIZE, buf + SW_HTTP2_FRAME_HEADER_SIZE, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE);
if (!send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE))
{
return SW_ERROR;
}
}
return SW_CONTINUE;
}
case SW_HTTP2_TYPE_GOAWAY:
{
uint32_t server_last_stream_id = ntohl(*(uint32_t *) (buf));
buf += 4;
value = ntohl(*(uint32_t *) (buf));
buf += 4;
swHttp2FrameTraceLog(recv, "last_stream_id=%d, error_code=%d, opaque_data=[%.*s]", server_last_stream_id, value, (int) (length - SW_HTTP2_GOAWAY_SIZE), buf);
// update goaway error code and error msg
zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errCode"), value);
zend_update_property_stringl(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errMsg"), buf, length - SW_HTTP2_GOAWAY_SIZE);
zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("serverLastStreamId"), server_last_stream_id);
close();
return SW_CONTINUE;
}
case SW_HTTP2_TYPE_RST_STREAM:
{
value = ntohl(*(uint32_t *) (buf));
swHttp2FrameTraceLog(recv, "error_code=%d", value);
// delete and free quietly
swHashMap_del_int(streams, stream_id);
return SW_CONTINUE;
}
/**
* TODO not support push_promise
*/
case SW_HTTP2_TYPE_PUSH_PROMISE:
{
#ifdef SW_DEBUG
uint32_t promise_stream_id = ntohl(*(uint32_t *) (buf)) & 0x7fffffff;
swHttp2FrameTraceLog(recv, "promise_stream_id=%d", promise_stream_id);
#endif
// auto promise_stream = create_stream(promise_stream_id, false);
// RETVAL_ZVAL(promise_stream->response_object, 0, 0);
// return SW_READY;
return SW_CONTINUE;
}
default:
{
swHttp2FrameTraceLog(recv, "");
}
}
http2_client_stream *stream = get_stream(stream_id);
// The stream is not found or has closed
if (stream == NULL)
{
swNotice("http2 stream#%d belongs to an unknown type or it never registered", stream_id);
return SW_CONTINUE;
}
if (type == SW_HTTP2_TYPE_HEADERS)
{
parse_header(stream, flags, buf, length);
}
else if (type == SW_HTTP2_TYPE_DATA)
{
if (length > 0)
{
if (!stream->buffer)
{
stream->buffer = swString_new(SW_HTTP2_DATA_BUFFER_SIZE);
}
#ifdef SW_HAVE_ZLIB
if (stream->gzip)
{
if (php_swoole_zlib_uncompress(&stream->gzip_stream, stream->gzip_buffer, buf, length) == SW_ERR)
{
swWarn("uncompress failed");
return SW_ERROR;
}
swString_append_ptr(stream->buffer, stream->gzip_buffer->str, stream->gzip_buffer->length);
}
else
#endif
{
swString_append_ptr(stream->buffer, buf, length);
}
// now we control the connection flow only (not stream)
// our window size is unlimited, so we don't worry about subtraction overflow
local_settings.window_size -= length;
stream->local_window_size -= length;
if (local_settings.window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4))
{
if (!send_window_update(0, SW_HTTP2_MAX_WINDOW_SIZE - local_settings.window_size))
{
return SW_ERROR;
}
local_settings.window_size = SW_HTTP2_MAX_WINDOW_SIZE;
}
if (stream->local_window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4))
{
if (!send_window_update(stream_id, SW_HTTP2_MAX_WINDOW_SIZE - stream->local_window_size))
{
return SW_ERROR;
}
stream->local_window_size = SW_HTTP2_MAX_WINDOW_SIZE;
}
}
}
uint8_t stream_type = stream->type;
if (
(type == SW_HTTP2_TYPE_DATA && stream_type == SW_HTTP2_STREAM_PIPELINE) || // pipeline data frame
(stream_type == SW_HTTP2_STREAM_NORMAL && (flags & SW_HTTP2_FLAG_END_STREAM)) || // normal end frame
type == SW_HTTP2_TYPE_RST_STREAM || type == SW_HTTP2_TYPE_GOAWAY // rst or goaway frame
)
{
zval zresponse = *stream->zresponse;
if (type == SW_HTTP2_TYPE_RST_STREAM)
{
zend_update_property_long(swoole_http2_response_ce, &zresponse, ZEND_STRL("statusCode"), -3);
zend_update_property_long(swoole_http2_response_ce, &zresponse, ZEND_STRL("errCode"), value);
}
else if (stream_type == SW_HTTP2_STREAM_PIPELINE && !(flags & SW_HTTP2_FLAG_END_STREAM))
{
zend_update_property_bool(swoole_http2_response_ce, &zresponse, ZEND_STRL("pipeline"), 1);
}
if (stream->buffer)
{
zend_update_property_stringl(swoole_http2_response_ce, stream->zresponse, ZEND_STRL("data"), stream->buffer->str, stream->buffer->length);
swString_clear(stream->buffer);
}
Z_ADDREF_P(&zresponse);
swHashMap_del_int(streams, stream_id);
RETVAL_ZVAL(&zresponse, 0, 0);
return SW_READY;
}
return SW_CONTINUE;
}
#ifdef SW_HAVE_ZLIB
int php_swoole_zlib_uncompress(z_stream *stream, swString *buffer, char *body, int length)
{
int status = 0;
stream->avail_in = length;
stream->next_in = (Bytef *) body;
stream->total_in = 0;
stream->total_out = 0;
#if 0
printf(SW_START_LINE"\nstatus=%d\tavail_in=%ld,\tavail_out=%ld,\ttotal_in=%ld,\ttotal_out=%ld\n", status,
stream->avail_in, stream->avail_out, stream->total_in, stream->total_out);
#endif
swString_clear(buffer);
while (1)
{
stream->avail_out = buffer->size - buffer->length;
stream->next_out = (Bytef *) (buffer->str + buffer->length);
status = inflate(stream, Z_SYNC_FLUSH);
#if 0
printf("status=%d\tavail_in=%ld,\tavail_out=%ld,\ttotal_in=%ld,\ttotal_out=%ld,\tlength=%ld\n", status,
stream->avail_in, stream->avail_out, stream->total_in, stream->total_out, buffer->length);
#endif
if (status >= 0)
{
buffer->length = stream->total_out;
}
if (status == Z_STREAM_END)
{
return SW_OK;
}
else if (status == Z_OK)
{
if (buffer->length + 4096 >= buffer->size)
{
if (swString_extend(buffer, buffer->size * 2) < 0)
{
return SW_ERR;
}
}
if (stream->avail_in == 0)
{
return SW_OK;
}
}
else
{
return SW_ERR;
}
}
return SW_ERR;
}
#endif
static PHP_METHOD(swoole_http2_client_coro, __construct)
{
char *host;
size_t host_len;
zend_long port = 80;
zend_bool ssl = SW_FALSE;
ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 3)
Z_PARAM_STRING(host, host_len)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(port)
Z_PARAM_BOOL(ssl)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
if (host_len == 0)
{
zend_throw_exception(swoole_http2_client_coro_exception_ce, "host is empty", SW_ERROR_INVALID_PARAMS);
RETURN_FALSE;
}
http2_client *h2c = new http2_client(host, host_len, port, ssl, getThis());
if (ssl)
{
#ifndef SW_USE_OPENSSL
zend_throw_exception_ex(
swoole_http2_client_coro_exception_ce,
EPROTONOSUPPORT, "you must configure with `enable-openssl` to support ssl connection"
);
RETURN_FALSE;
#endif
}
swoole_set_object(getThis(), h2c);
zend_update_property_stringl(swoole_http2_client_coro_ce, getThis(), ZEND_STRL("host"), host, host_len);
zend_update_property_long(swoole_http2_client_coro_ce, getThis(), ZEND_STRL("port"), port);
zend_update_property_bool(swoole_http2_client_coro_ce, getThis(), ZEND_STRL("ssl"), ssl);
}
static PHP_METHOD(swoole_http2_client_coro, set)
{
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
zval *zset;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY(zset)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
zval *zsetting = sw_zend_read_and_convert_property_array(swoole_http2_client_coro_ce, getThis(), ZEND_STRL("setting"), 0);
php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset));
h2c->apply_setting(zset);
RETURN_TRUE;
}
bool http2_client::send_window_update(int stream_id, uint32_t size)
{
char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE];
swTraceLog(SW_TRACE_HTTP2, "[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, size);
*(uint32_t*) ((char *)frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(size);
swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, stream_id);
return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE);
}
bool http2_client::send_setting()
{
swHttp2_settings *settings = &local_settings;
uint16_t id = 0;
uint32_t value = 0;
char frame[SW_HTTP2_FRAME_HEADER_SIZE + 18];
memset(frame, 0, sizeof(frame));
swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_SETTINGS, 18, 0, 0);
char *p = frame + SW_HTTP2_FRAME_HEADER_SIZE;
/**
* MAX_CONCURRENT_STREAMS
*/
id = htons(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
memcpy(p, &id, sizeof(id));
p += 2;
value = htonl(settings->max_concurrent_streams);
memcpy(p, &value, sizeof(value));
p += 4;
/**
* MAX_FRAME_SIZE
*/
id = htons(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE);
memcpy(p, &id, sizeof(id));
p += 2;
value = htonl(settings->max_frame_size);
memcpy(p, &value, sizeof(value));
p += 4;
/**
* INIT_WINDOW_SIZE
*/
id = htons(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE);
memcpy(p, &id, sizeof(id));
p += 2;
value = htonl(settings->window_size);
memcpy(p, &value, sizeof(value));
p += 4;
swTraceLog(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN "]\t[length=%d]", swHttp2_get_type(SW_HTTP2_TYPE_SETTINGS), 18);
return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + 18);
}
int http2_client::parse_header(http2_client_stream *stream, int flags, char *in, size_t inlen)
{
zval *zresponse = stream->zresponse;
if (flags & SW_HTTP2_FLAG_PRIORITY)
{
//int stream_deps = ntohl(*(int *) (in));
//uint8_t weight = in[4];
in += 5;
inlen -= 5;
}
zval *zheaders = sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL("headers"), 0);
zval *zcookies = sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL("cookies"), 0);
zval *zset_cookie_headers = sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL("set_cookie_headers"), 0);
ssize_t rv;
while (true)
{
nghttp2_nv nv;
int inflate_flags = 0;
size_t proclen;
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, (uchar *) in, inlen, 1);
if (rv < 0)
{
nghttp2_error(rv, "nghttp2_hd_inflate_hd failed");
return SW_ERR;
}
proclen = (size_t) rv;
in += proclen;
inlen -= proclen;
//swTraceLog(SW_TRACE_HTTP2, "Header: %s[%d]: %s[%d]", nv.name, nv.namelen, nv.value, nv.valuelen);
if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)
{
if (nv.name[0] == ':')
{
if (strncasecmp((char *) nv.name + 1, "status", nv.namelen -1) == 0)
{
zend_update_property_long(swoole_http2_response_ce, zresponse, ZEND_STRL("statusCode"), atoi((char *) nv.value));
continue;
}
}
#ifdef SW_HAVE_ZLIB
else if (strncasecmp((char *) nv.name, "content-encoding", nv.namelen) == 0 && strncasecmp((char *) nv.value, "gzip", nv.valuelen) == 0)
{
/**
* init zlib stream
*/
stream->gzip = 1;
memset(&stream->gzip_stream, 0, sizeof(stream->gzip_stream));
stream->gzip_buffer = swString_new(8192);
stream->gzip_stream.zalloc = php_zlib_alloc;
stream->gzip_stream.zfree = php_zlib_free;
/**
* zlib decode
*/
if (Z_OK != inflateInit2(&stream->gzip_stream, MAX_WBITS + 16))
{
swWarn("inflateInit2() failed");
return SW_ERR;
}
}
#endif
else if (strncasecmp((char *) nv.name, "set-cookie", nv.namelen) == 0)
{
if (SW_OK != http_parse_set_cookies((char *) nv.value, nv.valuelen, zcookies, zset_cookie_headers))
{
return SW_ERR;
}
}
add_assoc_stringl_ex(zheaders, (char *) nv.name, nv.namelen, (char *) nv.value, nv.valuelen);
}
if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL)
{
nghttp2_hd_inflate_end_headers(inflater);
break;
}
if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0)
{
break;
}
}
return SW_OK;
}
static ssize_t http2_client_build_header(zval *zobject, zval *zrequest, char *buffer)
{
http2_client *h2c = (http2_client *) swoole_get_object(zobject);
zval *zmethod = sw_zend_read_property(swoole_http2_request_ce, zrequest, ZEND_STRL("method"), 0);
zval *zpath = sw_zend_read_property(swoole_http2_request_ce, zrequest, ZEND_STRL("path"), 0);
zval *zheaders = sw_zend_read_property(swoole_http2_request_ce, zrequest, ZEND_STRL("headers"), 0);
zval *zcookies = sw_zend_read_property(swoole_http2_request_ce, zrequest, ZEND_STRL("cookies"), 0);
http2::headers headers(8 + php_swoole_array_length_safe(zheaders) + php_swoole_array_length_safe(zcookies));
bool find_host = 0;
if (Z_TYPE_P(zmethod) != IS_STRING || Z_STRLEN_P(zmethod) == 0)
{
headers.add(ZEND_STRL(":method"), ZEND_STRL("GET"));
}
else
{
headers.add(ZEND_STRL(":method"), Z_STRVAL_P(zmethod), Z_STRLEN_P(zmethod));
}
if (Z_TYPE_P(zpath) != IS_STRING || Z_STRLEN_P(zpath) == 0)
{
headers.add(ZEND_STRL(":path"), "/", 1);
}
else
{
headers.add(ZEND_STRL(":path"), Z_STRVAL_P(zpath), Z_STRLEN_P(zpath));
}
if (h2c->ssl)
{
headers.add(ZEND_STRL(":scheme"), ZEND_STRL("https"));
}
else
{
headers.add(ZEND_STRL(":scheme"), ZEND_STRL("http"));
}
// Host
headers.reserve_one();
if (ZVAL_IS_ARRAY(zheaders))
{
zend_string *key;
zval *zvalue;
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), key, zvalue)
{
if (UNEXPECTED(!key || *ZSTR_VAL(key) == ':' || ZVAL_IS_NULL(zvalue)))
{
continue;
}
zend::string str_value(zvalue);
if (strncasecmp("host", ZSTR_VAL(key), ZSTR_LEN(key)) == 0)
{
headers.add(HTTP2_CLIENT_HOST_HEADER_INDEX, ZEND_STRL(":authority"), str_value.val(), str_value.len());
find_host = true;
}
else
{
headers.add(ZSTR_VAL(key), ZSTR_LEN(key), str_value.val(), str_value.len());
}
}
ZEND_HASH_FOREACH_END();
}
if (!find_host)
{
headers.add(HTTP2_CLIENT_HOST_HEADER_INDEX,ZEND_STRL(":authority"), h2c->host.c_str(), h2c->host.length());
}
// http cookies
if (ZVAL_IS_ARRAY(zcookies))
{
zend_string *key;
zval *zvalue;
char *encoded_value;
int encoded_value_len;
swString *buffer = SwooleTG.buffer_stack;
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zcookies), key, zvalue)
{
if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue)))
{
continue;
}
zend::string str_value(zvalue);
swString_clear(buffer);
swString_append_ptr(buffer, ZSTR_VAL(key), ZSTR_LEN(key));
swString_append_ptr(buffer, "=", 1);
encoded_value = php_swoole_url_encode(str_value.val(), str_value.len(), &encoded_value_len);
if (encoded_value)
{
swString_append_ptr(buffer, encoded_value, encoded_value_len);
efree(encoded_value);
headers.add(ZEND_STRL("cookie"), buffer->str, buffer->length);
}
}
ZEND_HASH_FOREACH_END();
}
size_t buflen = nghttp2_hd_deflate_bound(h2c->deflater, headers.get(), headers.len());
if (buflen > h2c->remote_settings.max_header_list_size)
{
php_swoole_error(E_WARNING, "header cannot bigger than remote max_header_list_size %u", h2c->remote_settings.max_header_list_size);
return -1;
}
ssize_t rv = nghttp2_hd_deflate_hd(h2c->deflater, (uchar *) buffer, buflen, headers.get(), headers.len());
if (rv < 0)
{
h2c->nghttp2_error(rv, "nghttp2_hd_deflate_hd() failed");
return -1;
}
return rv;
}
static void http2_client_stream_free(void *ptr)
{
http2_client_stream *stream = (http2_client_stream *) ptr;
if (stream->buffer)
{
swString_free(stream->buffer);
}
#ifdef SW_HAVE_ZLIB
if (stream->gzip)
{
inflateEnd(&stream->gzip_stream);
swString_free(stream->gzip_buffer);
}
#endif
if (stream->zresponse)
{
zval_ptr_dtor(stream->zresponse);
stream->zresponse = NULL;
}
efree(stream);
}
http2_client_stream* http2_client::create_stream(uint32_t stream_id, bool pipeline)
{
// malloc
http2_client_stream *stream = (http2_client_stream *) ecalloc(1, sizeof(http2_client_stream));
// init
stream->zresponse = &stream->_zresponse;
object_init_ex(stream->zresponse, swoole_http2_response_ce);
stream->stream_id = stream_id;
stream->type = pipeline ? SW_HTTP2_STREAM_PIPELINE : SW_HTTP2_STREAM_NORMAL;
stream->remote_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE;
stream->local_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE;
// add to map
swHashMap_add_int(streams, stream_id, stream);
// set property
zend_update_property_long(swoole_http2_response_ce, stream->zresponse, ZEND_STRL("streamId"), stream_id);
return stream;
}
bool http2_client::send_ping_frame()
{
char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE];
swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0);
return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE);
}
uint32_t http2_client::send_request(zval *req)
{
ssize_t length;
zval *zheaders = sw_zend_read_and_convert_property_array(swoole_http2_request_ce, req, ZEND_STRL("headers"), 0);
zval *zdata = sw_zend_read_property(swoole_http2_request_ce, req, ZEND_STRL("data"), 0);
zval *zpipeline = sw_zend_read_property(swoole_http2_request_ce, req, ZEND_STRL("pipeline"), 0);
bool is_data_empty = !zend_is_true(zdata);
if (ZVAL_IS_ARRAY(zdata))
{
add_assoc_stringl_ex(zheaders, ZEND_STRL("content-type"), (char *) ZEND_STRL("application/x-www-form-urlencoded"));
}
/**
* send header
*/
char* buffer = SwooleTG.buffer_stack->str;
length = http2_client_build_header(zobject, req, buffer + SW_HTTP2_FRAME_HEADER_SIZE);
if (length <= 0)
{
return 0;
}
auto stream = create_stream(stream_id, Z_BVAL_P(zpipeline));
if (is_data_empty)
{
//pipeline
if (stream->type == SW_HTTP2_STREAM_PIPELINE)
{
swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, length, SW_HTTP2_FLAG_END_HEADERS, stream->stream_id);
}
else
{
swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, length, SW_HTTP2_FLAG_END_STREAM | SW_HTTP2_FLAG_END_HEADERS, stream->stream_id);
}
}
else
{
swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, length, SW_HTTP2_FLAG_END_HEADERS, stream->stream_id);
}
swTraceLog(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN ", STREAM#%d] length=%zd", swHttp2_get_type(SW_HTTP2_TYPE_HEADERS), stream->stream_id, length);
if (!send(buffer, length + SW_HTTP2_FRAME_HEADER_SIZE))
{
return 0;
}
/**
* send body
*/
if (!is_data_empty)
{
char *p;
size_t len;
smart_str formstr_s = { NULL, 0 };
uint8_t send_flag;
uint32_t send_len;
zend::string str_zpost_data;
int flag = stream->type == SW_HTTP2_STREAM_PIPELINE ? 0 : SW_HTTP2_FLAG_END_STREAM;
if (ZVAL_IS_ARRAY(zdata))
{
p = php_swoole_http_build_query(zdata, &len, &formstr_s);
if (p == NULL)
{
php_swoole_error(E_WARNING, "http_build_query failed");
return 0;
}
}
else
{
str_zpost_data = zdata;
p = str_zpost_data.val();
len = str_zpost_data.len();
}
swTraceLog(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN ", END, STREAM#%d] length=%zu", swHttp2_get_type(SW_HTTP2_TYPE_DATA), stream->stream_id, len);
while (len > 0)
{
if (len > remote_settings.max_frame_size)
{
send_len = remote_settings.max_frame_size;
send_flag = 0;
}
else
{
send_len = len;
send_flag = flag;
}
swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_DATA, send_len, send_flag, stream->stream_id);
if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE))
{
return 0;
}
if (!send(p, send_len))
{
return 0;
}
len -= send_len;
p += send_len;
}
if (formstr_s.s)
{
smart_str_free(&formstr_s);
}
}
stream_id += 2;
return stream->stream_id;
}
bool http2_client::send_data(uint32_t stream_id, zval *data, bool end)
{
char buffer[SW_HTTP2_FRAME_HEADER_SIZE];
http2_client_stream *stream = get_stream(stream_id);
if (stream == NULL || stream->type != SW_HTTP2_STREAM_PIPELINE)
{
update_error_properties(EINVAL, cpp_string::format("can not found stream#%u", stream_id).c_str());
return false;
}
int flag = end ? SW_HTTP2_FLAG_END_STREAM : 0;
if (ZVAL_IS_ARRAY(data))
{
size_t len;
smart_str formstr_s = { 0 };
char *formstr = php_swoole_http_build_query(data, &len, &formstr_s);
if (formstr == NULL)
{
php_swoole_error(E_WARNING, "http_build_query failed");
return false;
}
memset(buffer, 0, SW_HTTP2_FRAME_HEADER_SIZE);
swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_DATA, len, flag, stream_id);
swTraceLog(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN ", END, STREAM#%d] length=%zu", swHttp2_get_type(SW_HTTP2_TYPE_DATA), stream_id, len);
if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE) || !send(formstr, len))
{
return false;
}
smart_str_free(&formstr_s);
}
else if (Z_TYPE_P(data) == IS_STRING)
{
swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_DATA, Z_STRLEN_P(data), flag, stream_id);
swTraceLog(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN ", END, STREAM#%d] length=%zu", swHttp2_get_type(SW_HTTP2_TYPE_DATA), stream_id, Z_STRLEN_P(data));
if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE) || !send(Z_STRVAL_P(data), Z_STRLEN_P(data)))
{
return false;
}
}
else
{
php_swoole_error(E_WARNING, "unknown data type[%d]", Z_TYPE_P(data) );
return false;
}
return true;
}
bool http2_client::send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len)
{
size_t length = SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE + debug_data_len;
char *frame = (char *) ecalloc(1, length);
bool ret;
swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_GOAWAY, SW_HTTP2_GOAWAY_SIZE + debug_data_len, error_code, 0);
*(uint32_t*) (frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(last_stream_id);
*(uint32_t*) (frame + SW_HTTP2_FRAME_HEADER_SIZE + 4) = htonl(error_code);
if (debug_data_len > 0)
{
memcpy(frame + SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE, debug_data, debug_data_len);
}
swTraceLog(SW_TRACE_HTTP2, "[" SW_ECHO_GREEN "] Send: last-sid=%d, error-code=%d", swHttp2_get_type(SW_HTTP2_TYPE_GOAWAY), last_stream_id, error_code);
ret = send(frame, length);
efree(frame);
return ret;
}
static PHP_METHOD(swoole_http2_client_coro, send)
{
zval *request;
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
if (!h2c->is_available())
{
RETURN_FALSE;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &request) == FAILURE)
{
RETURN_FALSE;
}
if (Z_TYPE_P(request) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(request), swoole_http2_request_ce))
{
php_swoole_fatal_error(E_ERROR, "object is not instanceof swoole_http2_request");
RETURN_FALSE;
}
uint32_t stream_id = h2c->send_request(request);
if (stream_id == 0)
{
RETURN_FALSE;
}
else
{
RETURN_LONG(stream_id);
}
}
static PHP_METHOD(swoole_http2_client_coro, recv)
{
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
if (!h2c->is_available())
{
RETURN_FALSE;
}
double timeout = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|d", &timeout) == FAILURE)
{
RETURN_FALSE;
}
while (true)
{
if (!h2c->recv_packet(timeout))
{
RETURN_FALSE;
}
enum swReturnType ret = h2c->parse_frame(return_value);
if (ret == SW_CONTINUE)
{
continue;
}
else if (ret == SW_READY)
{
break;
}
else
{
RETURN_FALSE;
}
}
}
static PHP_METHOD(swoole_http2_client_coro, __destruct)
{
SW_PREVENT_USER_DESTRUCT();
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
if (!h2c)
{
return;
}
delete h2c;
swoole_set_object(getThis(), nullptr);
}
static PHP_METHOD(swoole_http2_client_coro, close)
{
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
RETURN_BOOL(h2c->close());
}
static PHP_METHOD(swoole_http2_client_coro, connect)
{
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
RETURN_BOOL(h2c->connect());
}
static sw_inline void http2_settings_to_array(swHttp2_settings *settings, zval* zarray)
{
array_init(zarray);
add_assoc_long_ex(zarray, ZEND_STRL("header_table_size"), settings->header_table_size);
add_assoc_long_ex(zarray, ZEND_STRL("window_size"), settings->window_size);
add_assoc_long_ex(zarray, ZEND_STRL("max_concurrent_streams"), settings->max_concurrent_streams);
add_assoc_long_ex(zarray, ZEND_STRL("max_frame_size"), settings->max_frame_size);
add_assoc_long_ex(zarray, ZEND_STRL("max_header_list_size"), settings->max_header_list_size);
}
static PHP_METHOD(swoole_http2_client_coro, stats)
{
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
zval _zarray, *zarray = &_zarray;
swString key = {0};
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &key.str, &key.length) == FAILURE)
{
RETURN_FALSE;
}
if (key.length > 0)
{
if (strcmp(key.str, "current_stream_id") == 0)
{
RETURN_LONG(h2c->stream_id);
}
else if (strcmp(key.str, "last_stream_id") == 0)
{
RETURN_LONG(h2c->last_stream_id);
}
else if (strcmp(key.str, "local_settings") == 0)
{
http2_settings_to_array(&h2c->local_settings, zarray);
RETURN_ZVAL(zarray, 0, 0);
}
else if (strcmp(key.str, "remote_settings") == 0)
{
http2_settings_to_array(&h2c->remote_settings, zarray);
RETURN_ZVAL(zarray, 0, 0);
}
else if (strcmp(key.str, "active_stream_num") == 0)
{
RETURN_LONG(h2c->streams ? swHashMap_count(h2c->streams) : 0);
}
}
else
{
array_init(return_value);
add_assoc_long_ex(return_value, ZEND_STRL("current_stream_id"), h2c->stream_id);
add_assoc_long_ex(return_value, ZEND_STRL("last_stream_id"), h2c->last_stream_id);
http2_settings_to_array(&h2c->local_settings, zarray);
add_assoc_zval_ex(return_value, ZEND_STRL("local_settings"), zarray);
http2_settings_to_array(&h2c->remote_settings, zarray);
add_assoc_zval_ex(return_value, ZEND_STRL("remote_settings"), zarray);
add_assoc_long_ex(return_value, ZEND_STRL("active_stream_num"), h2c->streams ? swHashMap_count(h2c->streams) : 0);
}
}
static PHP_METHOD(swoole_http2_client_coro, isStreamExist)
{
zend_long stream_id = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &stream_id) == FAILURE)
{
RETURN_FALSE;
}
if (stream_id < 0)
{
RETURN_FALSE;
}
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
if (!h2c->client)
{
RETURN_FALSE;
}
else
{
if (stream_id == 0)
{
RETURN_TRUE;
}
if (!h2c->streams)
{
RETURN_FALSE;
}
}
http2_client_stream *stream = h2c->get_stream(stream_id);
RETURN_BOOL(stream ? 1 : 0);
}
static PHP_METHOD(swoole_http2_client_coro, write)
{
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
if (!h2c->is_available())
{
RETURN_FALSE;
}
long stream_id;
zval *data;
zend_bool end = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz|b", &stream_id, &data, &end) == FAILURE)
{
RETURN_FALSE;
}
RETURN_BOOL(h2c->send_data(stream_id, data, end));
}
static PHP_METHOD(swoole_http2_client_coro, ping)
{
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
if (!h2c->is_available())
{
RETURN_FALSE;
}
RETURN_BOOL(h2c->send_ping_frame());
}
/**
* +-+-------------------------------------------------------------+
* |R| Last-Stream-ID (31) |
* +-+-------------------------------------------------------------+
* | Error Code (32) |
* +---------------------------------------------------------------+
* | Additional Debug Data (*) |
* +---------------------------------------------------------------+
*/
static PHP_METHOD(swoole_http2_client_coro, goaway)
{
http2_client *h2c = (http2_client *) swoole_get_object(getThis());
zend_long error_code = SW_HTTP2_ERROR_NO_ERROR;
char* debug_data = NULL;
size_t debug_data_len = 0;
if (!h2c->is_available())
{
RETURN_FALSE;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &error_code, &debug_data, &debug_data_len) == FAILURE)
{
RETURN_FALSE;
}
RETURN_BOOL(h2c->send_goaway_frame(error_code, debug_data, debug_data_len));
}
#endif

Comment ( 0 )

You need to Sign in for post a comment

Help Search

Gitee_sixth 5th_float_left_close