@@ -113,10 +113,14 @@ class Client {
113
113
114
114
/* options */
115
115
uint8_t max_retries = 0 ;
116
- bool keep_alive = true ; // enable by default
117
- bool websocket = false ; // if upgrade successfully
118
- bool chunked = false ; // Transfer-Encoding: chunked
119
- bool websocket_mask = true ; // enable websocket mask
116
+ bool keep_alive = true ; // enable by default
117
+ bool websocket = false ; // if upgrade successfully
118
+ bool chunked = false ; // Transfer-Encoding: chunked
119
+ bool websocket_mask = true ; // enable websocket mask
120
+ bool open_websocket_ping_frame = false ; // handle websocket ping frame by user
121
+ bool open_websocket_pong_frame = false ; // handle websocket pong frame by user
122
+ bool open_websocket_close_frame = false ; // handle websocket close frame by user
123
+ bool send_close_frame = false ; // send close frame to server
120
124
bool body_decompression = true ;
121
125
bool http_compression = true ;
122
126
#ifdef SW_HAVE_ZLIB
@@ -140,6 +144,7 @@ class Client {
140
144
* allowing access to the sent Request data even after the connection has been closed.
141
145
*/
142
146
String *tmp_write_buffer = nullptr ;
147
+ String *websocket_buffer = nullptr ;
143
148
bool connection_close = false ;
144
149
bool completed = false ;
145
150
bool event_stream = false ;
@@ -789,6 +794,15 @@ void Client::apply_setting(zval *zset, const bool check_all) {
789
794
delete write_func;
790
795
write_func = sw_callable_create (ztmp);
791
796
}
797
+ if (php_swoole_array_get_value (vht, " open_websocket_ping_frame" , ztmp)) {
798
+ open_websocket_ping_frame = zval_is_true (ztmp);
799
+ }
800
+ if (php_swoole_array_get_value (vht, " open_websocket_pong_frame" , ztmp)) {
801
+ open_websocket_pong_frame = zval_is_true (ztmp);
802
+ }
803
+ if (php_swoole_array_get_value (vht, " open_websocket_close_frame" , ztmp)) {
804
+ open_websocket_close_frame = zval_is_true (ztmp);
805
+ }
792
806
}
793
807
if (socket) {
794
808
php_swoole_socket_set (socket, zset);
@@ -1534,26 +1548,76 @@ bool Client::recv_websocket_frame(zval *zframe, double timeout) {
1534
1548
SW_ASSERT (websocket);
1535
1549
ZVAL_FALSE (zframe);
1536
1550
1537
- ssize_t retval = socket->recv_packet (timeout);
1538
- if (retval <= 0 ) {
1539
- php_swoole_socket_set_error_properties (zobject, socket);
1540
- zend::object_set (zobject, ZEND_STRL (" statusCode" ), HTTP_ESTATUS_SERVER_RESET);
1541
- if (socket->errCode != ETIMEDOUT) {
1542
- close ();
1543
- }
1544
- return false ;
1545
- } else {
1546
- String msg;
1547
- msg.length = retval;
1548
- msg.str = socket->get_read_buffer ()->str ;
1551
+ bool allow_uncompress = 0 ;
1549
1552
#ifdef SW_HAVE_ZLIB
1550
- php_swoole_websocket_frame_unpack_ex (&msg, zframe, accept_websocket_compression);
1551
- #else
1552
- php_swoole_websocket_frame_unpack (&msg, zframe);
1553
+ allow_uncompress = accept_websocket_compression;
1553
1554
#endif
1555
+
1556
+ if (!websocket_buffer) {
1557
+ websocket_buffer = new String ();
1558
+ }
1559
+
1560
+ do {
1561
+ if (sw_unlikely (!socket)) {
1562
+ return false ;
1563
+ }
1564
+
1565
+ ssize_t retval = socket->recv_packet (timeout);
1566
+ if (retval > 0 ) {
1567
+ String message = {};
1568
+ message.length = retval;
1569
+ message.str = socket->get_read_buffer ()->str ;
1570
+
1571
+ uchar flags = 0 ;
1572
+ uchar opcode = 0 ;
1573
+
1574
+ int result = php_swoole_websocket_frame_unpack (websocket_buffer,
1575
+ &message,
1576
+ zframe,
1577
+ &opcode,
1578
+ &flags,
1579
+ open_websocket_ping_frame,
1580
+ open_websocket_pong_frame,
1581
+ allow_uncompress);
1582
+
1583
+ if (sw_unlikely (result == SW_ERR)) {
1584
+ return false ;
1585
+ }
1586
+
1587
+ if (opcode == WebSocket::OPCODE_PING && !open_websocket_ping_frame) {
1588
+ push (zframe, WebSocket::OPCODE_PONG, WebSocket::FLAG_FIN);
1589
+ zval_ptr_dtor (zframe);
1590
+ ZVAL_FALSE (zframe);
1591
+ continue ;
1592
+ }
1593
+
1594
+ if (opcode == WebSocket::OPCODE_PONG && !open_websocket_pong_frame) {
1595
+ continue ;
1596
+ }
1597
+
1598
+ if (opcode == WebSocket::OPCODE_CLOSE && !open_websocket_close_frame) {
1599
+ if (!send_close_frame && push (zframe, WebSocket::OPCODE_CLOSE, WebSocket::FLAG_FIN)) {
1600
+ close ();
1601
+ }
1602
+ zval_ptr_dtor (zframe);
1603
+ ZVAL_FALSE (zframe);
1604
+ return false ;
1605
+ }
1606
+ } else {
1607
+ php_swoole_socket_set_error_properties (zobject, socket);
1608
+ zend::object_set (zobject, ZEND_STRL (" statusCode" ), HTTP_ESTATUS_SERVER_RESET);
1609
+ if (socket->errCode != ETIMEDOUT) {
1610
+ close ();
1611
+ }
1612
+ return false ;
1613
+ }
1614
+ } while (!ZVAL_IS_OBJECT (zframe));
1615
+
1616
+ if (ZVAL_IS_OBJECT (zframe)) {
1554
1617
zend_update_property_long (swoole_websocket_frame_ce, SW_Z8_OBJ_P (zframe), ZEND_STRL (" fd" ), socket->get_fd ());
1555
1618
return true ;
1556
1619
}
1620
+ return false ;
1557
1621
}
1558
1622
1559
1623
bool Client::upgrade (const std::string &path) {
@@ -1578,7 +1642,7 @@ bool Client::upgrade(const std::string &path) {
1578
1642
}
1579
1643
1580
1644
bool Client::push (zval *zdata, zend_long opcode, uint8_t flags) {
1581
- if (!websocket) {
1645
+ if (!sw_unlikely ( websocket) ) {
1582
1646
swoole_set_last_error (SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED);
1583
1647
php_swoole_fatal_error (E_WARNING, " websocket handshake failed, cannot push data" );
1584
1648
zend_update_property_long (
@@ -1593,6 +1657,8 @@ bool Client::push(zval *zdata, zend_long opcode, uint8_t flags) {
1593
1657
String *buffer = socket->get_write_buffer ();
1594
1658
buffer->clear ();
1595
1659
if (php_swoole_websocket_frame_is_object (zdata)) {
1660
+ zval *ztmp = sw_zend_read_property_ex (swoole_websocket_frame_ce, zdata, SW_ZSTR_KNOWN (SW_ZEND_STR_OPCODE), 1 );
1661
+ opcode = zval_get_long (ztmp);
1596
1662
if (php_swoole_websocket_frame_object_pack (buffer, zdata, websocket_mask, accept_websocket_compression) < 0 ) {
1597
1663
return false ;
1598
1664
}
@@ -1603,13 +1669,17 @@ bool Client::push(zval *zdata, zend_long opcode, uint8_t flags) {
1603
1669
}
1604
1670
}
1605
1671
1606
- if (socket->send_all (buffer->str , buffer->length ) != (ssize_t ) buffer->length ) {
1672
+ if (opcode == WebSocket::OPCODE_CLOSE) {
1673
+ send_close_frame = true ;
1674
+ }
1675
+
1676
+ if (socket->send_all (buffer->str , buffer->length ) == (ssize_t ) buffer->length ) {
1677
+ return true ;
1678
+ } else {
1607
1679
php_swoole_socket_set_error_properties (zobject, socket);
1608
1680
zend::object_set (zobject, ZEND_STRL (" statusCode" ), HTTP_ESTATUS_SERVER_RESET);
1609
1681
close ();
1610
1682
return false ;
1611
- } else {
1612
- return true ;
1613
1683
}
1614
1684
}
1615
1685
@@ -1675,6 +1745,15 @@ bool Client::close(const bool should_be_reset) {
1675
1745
_socket->get_socket ()->close_wait = 1 ;
1676
1746
return true ;
1677
1747
}
1748
+
1749
+ if (websocket && !send_close_frame) {
1750
+ zval zpayload = {};
1751
+ const char *reason = " Normal closure" ;
1752
+ ZVAL_STRINGL (&zpayload, reason, strlen (reason));
1753
+ push (&zpayload, WebSocket::OPCODE_CLOSE, WebSocket::FLAG_FIN);
1754
+ zval_ptr_dtor (&zpayload);
1755
+ }
1756
+
1678
1757
zend_update_property_bool (Z_OBJCE_P (zobject), SW_Z8_OBJ_P (zobject), ZEND_STRL (" connected" ), 0 );
1679
1758
if (!_socket->close ()) {
1680
1759
php_swoole_socket_set_error_properties (zobject, _socket);
@@ -1691,6 +1770,7 @@ Client::~Client() {
1691
1770
delete body;
1692
1771
delete tmp_write_buffer;
1693
1772
delete write_func;
1773
+ delete websocket_buffer;
1694
1774
}
1695
1775
1696
1776
static sw_inline HttpClientObject *http_client_coro_fetch_object (zend_object *obj) {
0 commit comments