aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2023-11-26 13:31:05 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2023-12-02 20:14:23 +0300
commitf09bf9eafdcb4b2bad82a62500be79a0b9ce3b22 (patch)
treeb5f3d4ba5eeab36dbd8f14b4e94716bf26b83429
parent4e5b65706e1c89d6d2845bd1fe9e1a91569e97d8 (diff)
downloadlibmicrohttpd-f09bf9eafdcb4b2bad82a62500be79a0b9ce3b22.tar.gz
libmicrohttpd-f09bf9eafdcb4b2bad82a62500be79a0b9ce3b22.zip
test_upgrade: added checking of socket shutdown status
Currently enabled only for non-TLS connections
-rw-r--r--src/microhttpd/test_upgrade.c243
1 files changed, 219 insertions, 24 deletions
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c
index b30ad64b..8264e13b 100644
--- a/src/microhttpd/test_upgrade.c
+++ b/src/microhttpd/test_upgrade.c
@@ -172,6 +172,8 @@ _testErrorLog_func (const char *errDesc, const char *funcName, int lineNum)
172/* Could be increased to facilitate debugging */ 172/* Could be increased to facilitate debugging */
173static int test_timeout = 5; 173static int test_timeout = 5;
174 174
175static bool test_tls;
176
175static int verbose = 0; 177static int verbose = 0;
176 178
177static uint16_t global_port; 179static uint16_t global_port;
@@ -377,6 +379,8 @@ struct wr_socket
377 } t; 379 } t;
378 380
379 bool is_nonblocking; 381 bool is_nonblocking;
382
383 bool eof_recieved;
380#ifdef HTTPS_SUPPORT 384#ifdef HTTPS_SUPPORT
381 /** 385 /**
382 * TLS credentials 386 * TLS credentials
@@ -439,6 +443,7 @@ wr_create_plain_sckt (void)
439 return NULL; 443 return NULL;
440 } 444 }
441 s->t = wr_plain; 445 s->t = wr_plain;
446 s->eof_recieved = false;
442 s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 447 s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
443 s->is_nonblocking = false; 448 s->is_nonblocking = false;
444 if (MHD_INVALID_SOCKET != s->fd) 449 if (MHD_INVALID_SOCKET != s->fd)
@@ -467,6 +472,7 @@ wr_create_tls_sckt (void)
467 return NULL; 472 return NULL;
468 } 473 }
469 s->t = wr_tls; 474 s->t = wr_tls;
475 s->eof_recieved = false;
470 s->tls_connected = 0; 476 s->tls_connected = 0;
471 s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 477 s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
472 s->is_nonblocking = false; 478 s->is_nonblocking = false;
@@ -532,6 +538,7 @@ wr_create_from_plain_sckt (MHD_socket plain_sk)
532 return NULL; 538 return NULL;
533 } 539 }
534 s->t = wr_plain; 540 s->t = wr_plain;
541 s->eof_recieved = false;
535 s->fd = plain_sk; 542 s->fd = plain_sk;
536 s->is_nonblocking = false; /* The actual mode is unknown */ 543 s->is_nonblocking = false; /* The actual mode is unknown */
537 wr_make_nonblocking (s); /* Force set mode to have correct status */ 544 wr_make_nonblocking (s); /* Force set mode to have correct status */
@@ -540,6 +547,19 @@ wr_create_from_plain_sckt (MHD_socket plain_sk)
540} 547}
541 548
542 549
550/**
551 * Check whether shutdown of connection was received from remote
552 * @param s socket to check
553 * @return zero if shutdown signal has not been received,
554 * 1 if shutdown signal was already received
555 */
556static int
557wr_is_eof_received (struct wr_socket *s)
558{
559 return s->eof_recieved ? 1 : 0;
560}
561
562
543enum wr_wait_for_type 563enum wr_wait_for_type
544{ 564{
545 WR_WAIT_FOR_RECV = 0, 565 WR_WAIT_FOR_RECV = 0,
@@ -575,10 +595,13 @@ wr_wait_socket_ready_noabort_ (struct wr_socket *s,
575 else 595 else
576 tmo_ptr = NULL; /* No timeout */ 596 tmo_ptr = NULL; /* No timeout */
577 597
578 if (WR_WAIT_FOR_RECV == wait_for) 598 do
579 sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr); 599 {
580 else 600 if (WR_WAIT_FOR_RECV == wait_for)
581 sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr); 601 sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr);
602 else
603 sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr);
604 } while (0 > sel_res && MHD_SCKT_ERR_IS_EINTR_ (MHD_socket_get_error_ ()));
582 605
583 if (1 == sel_res) 606 if (1 == sel_res)
584 return true; 607 return true;
@@ -763,15 +786,19 @@ wr_send_tmo (struct wr_socket *s,
763{ 786{
764 if (wr_plain == s->t) 787 if (wr_plain == s->t)
765 { 788 {
766 int err; 789 ssize_t res;
767 ssize_t res = MHD_send_ (s->fd, buf, len); 790 while (! 0)
768 if (0 <= res) 791 {
769 return res; 792 int err;
770 err = MHD_socket_get_error_ (); 793 res = MHD_send_ (s->fd, buf, len);
771 if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err)) 794 if (0 <= res)
772 return res; 795 break; /* Success */
773 wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND); 796 err = MHD_socket_get_error_ ();
774 return MHD_send_ (s->fd, buf, len); 797 if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err))
798 break; /* Failure */
799 wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND);
800 }
801 return res;
775 } 802 }
776#ifdef HTTPS_SUPPORT 803#ifdef HTTPS_SUPPORT
777 else if (wr_tls == s->t) 804 else if (wr_tls == s->t)
@@ -846,15 +873,21 @@ wr_recv_tmo (struct wr_socket *s,
846{ 873{
847 if (wr_plain == s->t) 874 if (wr_plain == s->t)
848 { 875 {
849 int err; 876 ssize_t res;
850 ssize_t res = MHD_recv_ (s->fd, buf, len); 877 while (! 0)
851 if (0 <= res) 878 {
852 return res; 879 int err;
853 err = MHD_socket_get_error_ (); 880 res = MHD_recv_ (s->fd, buf, len);
854 if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err)) 881 if (0 == res)
855 return res; 882 s->eof_recieved = true;
856 wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV); 883 if (0 <= res)
857 return MHD_recv_ (s->fd, buf, len); 884 break; /* Success */
885 err = MHD_socket_get_error_ ();
886 if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err))
887 break; /* Failure */
888 wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV);
889 }
890 return res;
858 } 891 }
859#ifdef HTTPS_SUPPORT 892#ifdef HTTPS_SUPPORT
860 if (wr_tls == s->t) 893 if (wr_tls == s->t)
@@ -866,6 +899,8 @@ wr_recv_tmo (struct wr_socket *s,
866 while (1) 899 while (1)
867 { 900 {
868 ret = gnutls_record_recv (s->tls_s, buf, len); 901 ret = gnutls_record_recv (s->tls_s, buf, len);
902 if (0 == ret)
903 s->eof_recieved = true;
869 if (ret >= 0) 904 if (ret >= 0)
870 return ret; 905 return ret;
871 if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret)) 906 if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret))
@@ -910,6 +945,90 @@ wr_recv (struct wr_socket *s,
910 945
911 946
912/** 947/**
948 * Shutdown send/write on the socket.
949 * @param s the socket to shutdown
950 * @param how the type of shutdown: SHUT_WR or SHUT_RDWR
951 * @param timeout_ms the maximum wait time in milliseconds to receive the data,
952 * no limit if negative value is used
953 * @return zero on succeed, -1 otherwise
954 */
955static int
956wr_shutdown_tmo (struct wr_socket *s, int how, int timeout_ms)
957{
958 switch (how)
959 {
960 case SHUT_WR: /* Valid value */
961 break;
962 case SHUT_RDWR: /* Valid value */
963 break;
964 case SHUT_RD:
965 externalErrorExitDesc ("Unsupported 'how' value");
966 break;
967 default:
968 externalErrorExitDesc ("Invalid 'how' value");
969 break;
970 }
971 if (wr_plain == s->t)
972 {
973 (void) timeout_ms; /* Unused parameter for plain sockets */
974 return shutdown (s->fd, how);
975 }
976#ifdef HTTPS_SUPPORT
977 if (wr_tls == s->t)
978 {
979 ssize_t ret;
980 if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms))
981 return -1;
982
983 while (1)
984 {
985 ret =
986 gnutls_bye (s->tls_s,
987 (SHUT_WR == how) ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);
988 if (GNUTLS_E_SUCCESS == ret)
989 {
990#if 0 /* Disabled to test pure behaviour */
991 if (SHUT_RDWR == how)
992 (void) shutdown (s->fd, how); /* Also shutdown the underlying transport layer */
993#endif
994 return 0;
995 }
996 if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret))
997 break;
998 wr_wait_socket_ready_ (s, timeout_ms,
999 gnutls_record_get_direction (s->tls_s) ?
1000 WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
1001 }
1002
1003 fprintf (stderr, "The error returned by gnutls_bye() is "
1004 "'%s' ", gnutls_strerror ((int) ret));
1005#if GNUTLS_VERSION_NUMBER >= 0x020600
1006 fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
1007#else /* GNUTLS_VERSION_NUMBER < 0x020600 */
1008 fprintf (stderr, "(%d)\n", (int) ret);
1009#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */
1010 testErrorLogDesc ("gnutls_bye() failed with hard error");
1011 MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */
1012 return -1;
1013 }
1014#endif /* HTTPS_SUPPORT */
1015 return -1;
1016}
1017
1018
1019/**
1020 * Shutdown the socket.
1021 * @param s the socket to shutdown
1022 * @return zero on succeed, -1 otherwise
1023 */
1024static int
1025wr_shutdown (struct wr_socket *s, int how)
1026{
1027 return wr_shutdown_tmo (s, how, test_timeout * 1000);
1028}
1029
1030
1031/**
913 * Close socket and release allocated resourced 1032 * Close socket and release allocated resourced
914 * @param s the socket to close 1033 * @param s the socket to close
915 * @return zero on succeed, -1 otherwise 1034 * @return zero on succeed, -1 otherwise
@@ -1247,6 +1366,74 @@ recv_all (struct wr_socket *sock,
1247 1366
1248 1367
1249/** 1368/**
1369 * Shutdown write of the connection to signal end of transmission
1370 * for the remote side
1371 * @param sock the socket to shutdown
1372 */
1373static void
1374send_eof (struct wr_socket *sock)
1375{
1376 if (0 != wr_shutdown (sock, wr_is_eof_received (sock) ? SHUT_RDWR : SHUT_WR))
1377 externalErrorExitDesc ("Failed to shutdown connection");
1378}
1379
1380
1381/**
1382 * Receive end of the transmission indication from the remote side
1383 * @param sock the socket to use
1384 */
1385static void
1386receive_eof (struct wr_socket *sock)
1387{
1388 uint8_t buf[127];
1389 ssize_t ret;
1390 size_t rcvd;
1391 bool got_eof = false;
1392
1393 wr_make_nonblocking (sock);
1394 for (rcvd = 0; rcvd < sizeof(buf); rcvd += (size_t) ret)
1395 {
1396 ret = wr_recv (sock,
1397 buf + rcvd,
1398 sizeof(buf) - rcvd);
1399 if (0 > ret)
1400 {
1401 if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()) ||
1402 MHD_SCKT_ERR_IS_EINTR_ (MHD_socket_get_error_ ()))
1403 {
1404 ret = 0;
1405 continue;
1406 }
1407 externalErrorExitDesc ("recv() failed");
1408 }
1409 else if (0 == ret)
1410 {
1411 got_eof = true;
1412 break;
1413 }
1414 }
1415 if (got_eof && (0 == rcvd))
1416 return; /* Success */
1417
1418 if (0 != rcvd)
1419 {
1420 if (sizeof(buf) == rcvd)
1421 {
1422 fprintf (stderr, "Received at least %lu extra bytes while "
1423 "end-of-file is expected.\n", (unsigned long) sizeof(buf));
1424 mhdErrorExit ();
1425 }
1426 fprintf (stderr, "Received at %lu extra bytes and then %s"
1427 "end-of-file marker.\n", (unsigned long) rcvd,
1428 got_eof ? "" : "NO ");
1429 mhdErrorExit ();
1430 }
1431 if (! got_eof)
1432 mhdErrorExitDesc ("Failed to receive end-of-file marker.");
1433}
1434
1435
1436/**
1250 * Main function for the thread that runs the interaction with 1437 * Main function for the thread that runs the interaction with
1251 * the upgraded socket. 1438 * the upgraded socket.
1252 * 1439 *
@@ -1263,6 +1450,11 @@ run_usock (void *cls)
1263 "World"); 1450 "World");
1264 send_all_stext (usock, 1451 send_all_stext (usock,
1265 "Finished"); 1452 "Finished");
1453 if (! test_tls)
1454 {
1455 send_eof (usock);
1456 receive_eof (usock);
1457 }
1266 MHD_upgrade_action (urh, 1458 MHD_upgrade_action (urh,
1267 MHD_UPGRADE_ACTION_CLOSE); 1459 MHD_UPGRADE_ACTION_CLOSE);
1268 free (usock); 1460 free (usock);
@@ -1292,6 +1484,11 @@ run_usock_client (void *cls)
1292 "World"); 1484 "World");
1293 recv_all_stext (sock, 1485 recv_all_stext (sock,
1294 "Finished"); 1486 "Finished");
1487 if (! test_tls)
1488 {
1489 receive_eof (sock);
1490 send_eof (sock);
1491 }
1295 wr_close (sock); 1492 wr_close (sock);
1296 client_done = true; 1493 client_done = true;
1297 return NULL; 1494 return NULL;
@@ -1634,8 +1831,6 @@ run_mhd_loop (struct MHD_Daemon *daemon,
1634} 1831}
1635 1832
1636 1833
1637static bool test_tls;
1638
1639/** 1834/**
1640 * Test upgrading a connection. 1835 * Test upgrading a connection.
1641 * 1836 *